Refresher (1) As a refresher to statistical plots, let’s build a scatter plot with an additional statistic layer.
A dataset called movies_small is coded in your workspace. It is a random sample of 1000 observations from the larger movies dataset, that’s inside the ggplot2movies package. The dataset contains information on movies from IMDB. The variable votes is the number of IMDB users who have rated a movie and the rating (converted into a categorical variable) is the average rating for the movie.
# Create movies_small
library(ggplot2movies)
library(ggplot2)
set.seed(123)
movies_small <- movies[sample(nrow(movies), 1000), ]
movies_small$rating <- factor(round(movies_small$rating))
# Explore movies_small with str()
str(movies_small)
Classes 'tbl_df', 'tbl' and 'data.frame': 1000 obs. of 24 variables:
$ title : chr "Fair and Worm-er" "Shelf Life" "House: After Five Years of Living" "Three Long Years" ...
$ year : int 1946 2000 1955 2003 1963 1992 1999 1972 1994 1985 ...
$ length : int 7 4 11 76 103 107 87 84 127 94 ...
$ budget : int NA NA NA NA NA NA NA NA NA NA ...
$ rating : Factor w/ 10 levels "1","2","3","4",..: 7 7 6 8 8 5 4 8 5 5 ...
$ votes : int 16 11 15 11 103 28 105 9 37 28 ...
$ r1 : num 0 0 14.5 4.5 4.5 4.5 14.5 0 4.5 4.5 ...
$ r2 : num 0 0 0 0 4.5 0 4.5 0 4.5 0 ...
$ r3 : num 0 0 4.5 4.5 0 4.5 4.5 0 14.5 4.5 ...
$ r4 : num 0 0 4.5 0 4.5 4.5 4.5 0 4.5 14.5 ...
$ r5 : num 4.5 4.5 0 0 4.5 0 4.5 14.5 24.5 4.5 ...
$ r6 : num 4.5 24.5 34.5 4.5 4.5 0 14.5 0 4.5 14.5 ...
$ r7 : num 64.5 4.5 24.5 0 14.5 4.5 14.5 14.5 14.5 14.5 ...
$ r8 : num 14.5 24.5 4.5 4.5 14.5 24.5 14.5 24.5 14.5 14.5 ...
$ r9 : num 0 0 0 14.5 14.5 24.5 14.5 14.5 4.5 4.5 ...
$ r10 : num 14.5 24.5 14.5 44.5 44.5 24.5 14.5 44.5 4.5 24.5 ...
$ mpaa : chr "" "" "" "" ...
$ Action : int 0 0 0 0 0 0 0 0 0 0 ...
$ Animation : int 1 0 0 0 0 0 0 0 0 0 ...
$ Comedy : int 1 0 0 1 0 1 1 1 0 0 ...
$ Drama : int 0 0 0 0 1 0 0 0 1 1 ...
$ Documentary: int 0 0 1 0 0 0 0 0 0 0 ...
$ Romance : int 0 0 0 0 0 0 1 0 0 0 ...
$ Short : int 1 1 1 0 0 0 0 0 0 0 ...
# Build a scatter plot with mean and 95% CI
ggplot(movies_small, aes(x = rating, y = votes)) +
geom_point() +
stat_summary(fun.data = "mean_cl_normal",
geom = "crossbar",
width = 0.2,
col = "red") +
scale_y_log10()

Refresher (2) The plot in the graphics device is a variation on an oft-seen ggplot2 example using the diamonds dataset (containing information on several variables of over 50,000 diamonds).
Recall that there are a variety of scale_ functions. Here, data are transformed or filtered first, after which the plot and associated statistics are computed. For example, scale_y_continuous(limits = c(100, 1000) will remove values outside that range.
Contrast this to coord_cartesian(), which computes the statistics before plotting. That means that the plot and summary statistics are performed on the raw data. That’s why we say that coord_cartesian(c(100, 1000)) “zooms in” a plot. This was discussed in the chapter on coordinates in course 2.
Here we’re going to expand on this and introduce scale_x_log10() and scale_y_log10() which perform log10 transformations, and coord_equal(), which sets an aspect ratio of 1 (coord_fixed() is also an option).
Your task is to reproduce the plot in the viewer. Before you do this, it might be a good idea to explore diamonds in the console if you are not familiar with it.
# Reproduce the plot
ggplot(diamonds, aes(x = carat, y = price, col = color)) +
geom_point(alpha = 0.5, size = 0.5, shape = 16) +
scale_x_log10(expression(log[10](Carat)), limits = c(0.1,10)) +
scale_y_log10(expression(log[10](Price)), limits = c(100,100000)) +
scale_color_brewer(palette = "YlOrRd") +
coord_equal() +
theme_classic()

Refresher (3) The goal plot from the previous exercise is coded in your editor. Here you’ll expand on this plot with stat_smooth() model instead of showing every data point.
# Add smooth layer and facet the plot
ggplot(diamonds, aes(x = carat, y = price, col = color)) +
stat_smooth(method = "lm") +
scale_x_log10(expression(log[10](Carat)), limits = c(0.1,10)) +
scale_y_log10(expression(log[10](Price)), limits = c(100,100000)) +
scale_color_brewer(palette = "YlOrRd") +
coord_equal() +
theme_classic()

Transformations In this exercise you’ll return to the first plotting exercise and see how box plots compare to dot plots for representing high-density data.
Box plots are very useful, but they don’t solve all your problems all the time, for example, when your data are heavily skewed, you will still need to transform it. You’ll see that here, using the movies_small dataset, a subset of 10,000 observations of ggplot2movies::movies.
# movies_small is available
# Add a boxplot geom
d <- ggplot(movies_small, aes(x = rating, y = votes)) +
geom_point() +
geom_boxplot() +
stat_summary(fun.data = "mean_cl_normal",
geom = "crossbar",
width = 0.2,
col = "red")
# Untransformed plot
d

# Transform the scale
d + scale_y_log10()

# Transform the coordinates
d + coord_trans(y = "log10")
Error in if (zero_range(range)) { : missing value where TRUE/FALSE needed

Cut it up! If you only have continuous variables, you can convert them into ordinal variables using any of the following functions:
cut_interval(x, n) makes n groups from vector x with equal range. cut_number(x, n) makes n groups from vector x with (approximately) equal numbers of observations. cut_width(x, width) makes groups of width width from vector x. This is useful when you want to summarize a complex scatter plot like the one shown in the viewer. By applying these functions to the carat variable and mapping that onto the group aesthetic, you can convert the scatter plot in the viewer into a series of box plots on the fly.
# Plot object p
p <- ggplot(diamonds, aes(x = carat, y = price))
# Use cut_interval
p + geom_boxplot(aes(group = cut_interval(carat, n=10)))

# Use cut_number
p + geom_boxplot(aes(group = cut_number(carat, n=10)))

# Use cut_width
p + geom_boxplot(aes(group = cut_width(carat, width = 0.25)))

geom_density() To make a straightforward density plot, add a geom_density() layer.
Before plotting, you will calculate the emperical density function, similar to how you can use the density() function in the stats package, available by default when you start R. The following default parameters are used (you can specify these arguments both in density() as well as geom_density()):
bw = “nrd0”, telling R which rule to use to choose an appropriate bandwidth. kernel = “gaussian”, telling R to use the Gaussian kernel. We’ve already prepared a data frame test_data for you, containing three columns: norm, bimodal and uniform. Each column represents 200 samples from a normal, bimodal and uniform distribution.
rn <- rnorm(200, 0, 1)
bimodalDistFunc <- function (n,cpct, mu1, mu2, sig1, sig2) {
y0 <- rlnorm(n,mean=mu1, sd = sig1)
y1 <- rlnorm(n,mean=mu2, sd = sig2)
flag <- rbinom(n,size=1,prob=cpct)
y <- y0*(1 - flag) + y1*flag
}
bm <- bimodalDistFunc(n=200,0.4,-1,1, 1,1)
ud <- runif(200, -2, 1)
test_data <- data.frame("norm" = rn,
"bimodal" = bm,
"uniform" = ud)
head(test_data)
# test_data is available
# Calculating density: d
d <- density(test_data$norm)
# Use which.max() to calculate mode
mode <- d$x[which.max(d$y)]
# Finish the ggplot call
ggplot(test_data, aes(x = norm)) +
geom_rug() +
geom_density() +
geom_vline(xintercept = mode, col = "red")

Combine density plots and histogram Sometimes it is useful to compare a histogram with a density plot. However, the histogram’s y-scale must first be converted to frequency instead of absolute count. After doing so, you can add an empirical PDF using geom_density() or a theoretical PDF using stat_function().
Can you finish the plot below by following the steps?
# test_data is available
# Arguments you'll need later on
fun_args <- list(mean = mean(test_data$norm), sd = sd(test_data$norm))
# Finish the ggplot
ggplot(test_data, aes(x = norm)) +
geom_histogram(aes(y=..density..))+
geom_density(col = "red") +
stat_function(fun = dnorm, args = fun_args, col="blue")

Adjusting density plots There are three parameters that you may be tempted to adjust in a density plot:
bw - the smoothing bandwidth to be used, see ?density for details adjust - adjustment of the bandwidth, see density for details kernel - kernel used for density estimation, defined as “g” = gaussian “r” = rectangular “t” = triangular “e” = epanechnikov “b” = biweight “c” = cosine “o” = optcosine In this exercise you’ll use a dataset containing only four points, small_data, so that you can see how these three arguments affect the shape of the density plot.
The vector get_bw contains the bandwidth that is used by default in geom_density(). p is a basic plotting object that you can start from.
# small_data is available
small_data <- data.frame("x" = c(-3.5, 0.0,0.5, 6.0))
# Get the bandwith
get_bw <- density(small_data$x)$bw
# Basic plotting object
p <- ggplot(small_data, aes(x = x)) +
geom_rug() +
coord_cartesian(ylim = c(0,0.5))
# Create three plots
p + geom_density()

p + geom_density(adjust = 0.25)

p + geom_density(bw = 0.25 * get_bw)

# Create two plots
p + geom_density(kernel = "r")

p + geom_density(kernel = "e")

Box plots with varying width A drawback of showing a box plot per group, is that you don’t have any indication of the sample size, n, in each group, that went into making the plot. One way of dealing with this is to use a variable width for the box, which reflects differences in n.
Can you add some good-looking box plots to the basic plot coded on the right?
# Finish the plot
ggplot(diamonds, aes(x = cut, y = price, col = color)) +
geom_boxplot(varwidth = TRUE) +
facet_grid(. ~ color)

Mulitple density plots In this exercise you’ll combine multiple density plots. Here, you’ll combine just two distributions, a normal and a bimodal.
The first thing to remember is that you can consider values as two separate variables, like in the test_data data frame, or as a single continuous variable with their ID as a separate categorical variable, like in the test_data2 data frame. test_data2 is more convenient for combining and comparing multiple distributions.
test_data2 <- data.frame("dist" = c(rep("norm", 200), rep("bimodal", 200)),
"value" = c(test_data$norm, test_data$bimodal))
# test_data and test_data2 are available
str(test_data)
'data.frame': 200 obs. of 3 variables:
$ norm : num -0.602 -0.994 1.027 0.751 -1.509 ...
$ bimodal: num 0.986 1.232 3.668 0.414 0.094 ...
$ uniform: num 0.197 -0.171 -1.327 0.749 0.408 ...
str(test_data2)
'data.frame': 400 obs. of 2 variables:
$ dist : Factor w/ 2 levels "bimodal","norm": 2 2 2 2 2 2 2 2 2 2 ...
$ value: num -0.602 -0.994 1.027 0.751 -1.509 ...
# Plot with test_data
ggplot(test_data, aes(x = norm)) +
geom_rug()+
geom_density()

# Plot two distributions with test_data2
ggplot(test_data2, aes(x = value, fill = dist, col = dist)) +
geom_rug(alpha = 0.6) +
geom_density(alpha = 0.6)

Multiple density plots (2) When you looked at multiple box plots, you compared the total sleep time of various mammals, sorted according to their eating habits. One thing you noted is that for insectivores, box plots didn’t really make sense, since there were only 5 observations to begin with. You decided that you could nonetheless use the width of a box plot to show the difference in sample size between the groups. Here, you’ll see a similar thing with density plots.
A cleaned up version of the mammalian dataset is available as mammals.
head(msleep)
mammals <- msleep[,c("vore","sleep_total")]
mammals
# Individual densities
ggplot(mammals[mammals$vore == "Insecti", ], aes(x = sleep_total, fill = vore)) +
geom_density(col = NA, alpha = 0.35) +
scale_x_continuous(limits = c(0, 24)) +
coord_cartesian(ylim = c(0, 0.3))

# With faceting
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
geom_density(col = NA, alpha = 0.35) +
scale_x_continuous(limits = c(0, 24)) +
coord_cartesian(ylim = c(0, 0.3)) +
facet_wrap( ~ vore, nrow = 2)

# Note that by default, the x ranges fill the scale
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
geom_density(col = NA, alpha = 0.35) +
scale_x_continuous(limits = c(0, 24)) +
coord_cartesian(ylim = c(0, 0.3))

# Trim each density plot individually
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
geom_density(col = NA, alpha = 0.35, trim = TRUE) +
scale_x_continuous(limits=c(0,24)) +
coord_cartesian(ylim = c(0, 0.3))

Weighted density plots When plotting a single variable, the density plots (and their bandwidths) are calculated separate for each variable (see the plot from the previous exercise, provided).
However, when you compare several variables (such as eating habits) it’s useful to see the density of each subset in relation to the whole data set. This holds true for multiple density plots as well as for violin plots.
For this, we need to weight the density plots so that they’re relative to each other. Each density plot is adjusted according to what proportion of the total data set each sub-group represents. We calculated this using the dplyr commands on lines 11-15.
The mammals data frame is available as before. After executing the commnads, it will have the variable n, which we’ll use for weighting.
# Unweighted density plot from before
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
geom_density(col = NA, alpha = 0.35) +
scale_x_continuous(limits = c(0, 24)) +
coord_cartesian(ylim = c(0, 0.3))

# Unweighted violin plot
ggplot(mammals, aes(x = vore, y = sleep_total, fill = vore)) +
geom_violin()

# Calculate weighting measure
library(dplyr)
Attaching package: 'dplyr'
The following objects are masked from 'package:stats':
filter, lag
The following objects are masked from 'package:base':
intersect, setdiff, setequal, union
mammals2 <- mammals %>%
group_by(vore) %>%
mutate(n = n() / nrow(mammals)) -> mammals
# Weighted density plot
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
geom_density(aes(weight = n), col = NA, alpha = 0.35) +
scale_x_continuous(limits = c(0, 24)) +
coord_cartesian(ylim = c(0, 0.3))

# Weighted violin plot
ggplot(mammals, aes(x = vore, y = sleep_total, fill = vore)) +
geom_violin(aes(weight = n), col = NA)

2D density plots (1) You can consider two orthogonal density plots in the form of a 2D density plot. Just like with a 1D density plot, you can adjust the bandwidth of both axes independently.
The data is stored in the faithful data frame, available in the datasets package. The object p contains the base definitions of a plot.
# Base layers
p <- ggplot(faithful, aes(x = waiting, y = eruptions)) +
scale_y_continuous(limits = c(1, 5.5), expand = c(0, 0)) +
scale_x_continuous(limits = c(40, 100), expand = c(0, 0)) +
coord_fixed(60 / 4.5)
# 1 - Use geom_density_2d()
p + geom_density_2d()

# 2 - Use stat_density_2d() with arguments
p + stat_density_2d(aes(col = ..level..), h = c(5, 0.5))

2D density plots (2) Continuing with the density plots from the last exercise, here you’ll explore the viridis package. This package contains multi-hue color palettes suitable for continuous variables.
The advantage of these scales is that instead of providing an even color gradient for a continuous scale, they highlight the highest values by using an uneven color gradient on purpose. The high values are lighter colors (yellow versus blue), so they stand out more.
A shaded 2D density plot showing the same data as the previous exercise has been provided for you. Up to you to upgrade it!
# Load in the viridis package
library(viridis)
package 'viridis' was built under R version 3.4.4Loading required package: viridisLite
# Add viridis color scale
ggplot(faithful, aes(x = waiting, y = eruptions)) +
scale_y_continuous(limits = c(1, 5.5), expand = c(0,0)) +
scale_x_continuous(limits = c(40, 100), expand = c(0,0)) +
coord_fixed(60/4.5) +
stat_density_2d(geom = "tile", aes(fill = ..density..), h=c(5,.5), contour = FALSE)+ scale_fill_viridis()

Pair plots and correlation matrices On startup, R features two useful quick-and-dirty pairs plots functions. They both only take continuous variables.
You’ll be working with the iris dataset and with mtcars_fact, a version of mtcars where categorical variables have been converted into actual factor columns.
# pairs
pairs(iris[1:4])

# chart.Correlation
library(PerformanceAnalytics)
Loading required package: xts
Loading required package: zoo
Attaching package: 'zoo'
The following objects are masked from 'package:base':
as.Date, as.Date.numeric
Attaching package: 'xts'
The following objects are masked from 'package:dplyr':
first, last
Package PerformanceAnalytics (1.5.2) loaded.
Copyright (c) 2004-2018 Peter Carl and Brian G. Peterson, GPL-2 | GPL-3
https://github.com/braverock/PerformanceAnalytics
Attaching package: 'PerformanceAnalytics'
The following object is masked from 'package:graphics':
legend
chart.Correlation(iris[1:4])

# ggpairs
library(GGally)
Attaching package: 'GGally'
The following object is masked from 'package:dplyr':
nasa
ggpairs(iris[1:3])

Create a correlation matrix in ggplot2 Instead of using an off-the-shelf correlation matrix function, you can of course create your own plot. Just for fun, in this exercise, you’ll re-create the scatterplot you see on the right. The strength of the correlation is depicted by the size and color of the points and labels.
For starters, a correlation matrix can be calculated using, for example, cor(dataframe) (if all variables are numerical). Before you can use your data frame to create your own correlation matrix plot, you’ll need to get it in the right format.
In the editor, you can see the definition of cor_list(), a function that re-formats the data frame x. Here, L is used to add the points to the lower triangle of the matrix, and M is used to add the numerical values as text to the upper triangle of the matrix. With reshape2::melt(), the correlation matrices L and M are each converted into a three-column data frame: the x and y axes of the correlation matrix make up the first two columns and the corresponding correlation coefficient makes up the third column. These become the new variables “points” and “labels”, which can be mapped onto the size aesthetic for the points in the lower triangle and onto the label aesthetic for the text in the upper triangle, respectively. Their values will be the same, but their positions on the plot will be symmetrical about the diagonal! Merging L and M, you have everything you need.
If you’re not familiar with reshape2 - don’t worry, the only reason we use that instead of tidyr is that reshape2::melt() can handle a matrix, whereas tidyr::gather() requires a data frame. At this point you just need to understand how to use the output from cor_list().
You’ll first use dplyr to execute this function on the continuous variables in the iris data frame (the first four columns), but separately for each species. Please refer to the course on dplyr if you are not familiar with these functions.
Next, you’ll actually plot the resulting data frame with ggplot2 functions.
library(ggplot2)
library(reshape2)
cor_list <- function(x) {
L <- M <- cor(x)
M[lower.tri(M, diag = TRUE)] <- NA
M <- melt(M)
names(M)[3] <- "points"
L[upper.tri(L, diag = TRUE)] <- NA
L <- melt(L)
names(L)[3] <- "labels"
merge(M, L)
}
# Calculate xx with cor_list
library(dplyr)
xx <- iris %>%
group_by(Species) %>%
do(cor_list(.[1:4]))
# Finish the plot
ggplot(xx, aes(x = Var1, y = Var2)) +
geom_point(aes(col = points, size = abs(points)), shape = 16) +
geom_text(aes(col = labels, size = abs(labels), label = round(labels, 2))) +
scale_size(range = c(0, 6)) +
scale_color_gradient2("r", limits = c(-1, 1)) +
scale_y_discrete("", limits = rev(levels(xx$Var1))) +
scale_x_discrete("") +
guides(size = FALSE) +
geom_abline(slope = -1, intercept = nlevels(xx$Var1) + 1) +
coord_fixed() +
facet_grid(. ~ Species) +
theme(axis.text.y = element_text(angle = 45, hjust = 1),
axis.text.x = element_text(angle = 45, hjust = 1),
strip.background = element_blank())

Proportional/stacked bar plots Before you head over to ternary plots, let’s try to make a classical proportional/stacked bar plot of a subset of the data. We’ll use a stacked bar plot and the coord_flip() function to flips the x and y axes.
The data frame for the African Soil Profiles Database is available in your workspace as africa and can be found in the GSIF package. It contains three columns: Sand, Silt and Clay. A smaller version, containing only 50 observations is stored in africa_sample.
In the first course we mentioned that in the data layer, the structure of the data should reflect how you wish to plot it. For a ternary plot, you need to have three separate variables, for example, Sand, Silt and Clay in africa. However, for a proportional/stacked bar plot, you just need two. The type should be defined as three levels within a single factor variable. That is, you want tidy data.
It’s also useful to maintain the site IDs as a variable within the data frame, currently, they are stored at row names, which is poor style and not useful.
# Explore africa
str(africa)
'data.frame': 40093 obs. of 3 variables:
$ Sand: num 24 36 56 52 65 43 42 47 57 51 ...
$ Silt: num 12 14 18 21 3 14 22 19 15 14 ...
$ Clay: num 64 50 26 27 32 43 36 34 28 35 ...
africa_sample <- africa[sample(nrow(africa), 50), ]
str(africa_sample)
'data.frame': 50 obs. of 3 variables:
$ Sand: num 5 58 35 34 42 65 15 89 63 87 ...
$ Silt: num 17 26 28 10 14 8 43 4 3 6 ...
$ Clay: num 78 16 37 56 44 27 42 7 34 7 ...
# Add an ID column from the row.names
africa_sample$ID <- row.names(africa_sample)
# Gather africa_sample
library(tidyr)
Attaching package: 'tidyr'
The following object is masked from 'package:reshape2':
smiths
africa_sample_tidy <- gather(africa_sample, key, value, -ID)
head(africa_sample_tidy)
# Finish the ggplot command
ggplot(africa_sample_tidy, aes(x = factor(ID), y = value, fill = key)) +
geom_col() +
coord_flip()

Producing ternary plots Ok, let’s move onto ternary plots. For this you’ll use the ggtern package, which provides the ggtern() function.
In contrast to what you just saw in africa_small_tidy, the three soil properties, Sand, Silt and Clay, are not going to be located in a single variable. The distinction between wide and tidy format data was discussed in the first course and here you’ll see it in action. Sometimes you need to rearrange your data for the desired plot type.
Here, you’ll use the complete dataset, africa, containing three separate variables for the measures of interest: that format is perfect for a ternary plot.
# Load ggtern
library(ggtern)
--
Consider donating at: http://ggtern.com
Even small amounts (say $10-50) are very much appreciated!
Remember to cite, run citation(package = 'ggtern') for further info.
--
Attaching package: 'ggtern'
The following objects are masked from 'package:ggplot2':
%+%, aes, annotate, calc_element, ggplot, ggplotGrob, ggplot_build, ggplot_gtable, ggsave,
layer_data, theme, theme_bw, theme_classic, theme_dark, theme_gray, theme_light, theme_linedraw,
theme_minimal, theme_void
# Build ternary plot
ggtern(africa, aes(x = Sand, y = Silt, z = Clay)) +
geom_point(shape=16, alpha=0.2)

Adjusting ternary plots Ternary plots have been around for a while in R; you could achieve the same thing with the vcd package authored by Michael Friendly. If you just need a quick and dirty ternary plot, that may suit you just fine. However, since ggtern is built on ggplot2, you can take advantage of all the tools available therein.
ggtern is authored by Nicholas Hamilton, more information can be found on his package website: www.ggtern.com.
The plot from the previous exercise is available twice. Can you adapt it in different ways to make different ternary density plots?
# ggtern and ggplot2 are loaded
# Original plot:
ggtern(africa, aes(x = Sand, y = Silt, z = Clay)) +
geom_point(shape = 16, alpha = 0.2)

# Plot 1
ggtern(africa, aes(x = Sand, y = Silt, z = Clay)) +
geom_density_tern()

# Plot 2
ggtern(africa, aes(x = Sand, y = Silt, z = Clay)) +
stat_density_tern(geom = "polygon", aes(fill = ..level.., alpha = ..level..)) +
guides(fill = FALSE)

Build the network (1) Network data may be stored in a variety of ways.
For this example, you’ll use an undirected network of romantic relationships in the TV show Mad Men: geomnet::madmen.
# Load geomnet & examine structure of madmen
library(geomnet)
str(madmen)
List of 2
$ edges :'data.frame': 39 obs. of 2 variables:
..$ Name1: Factor w/ 9 levels "Betty Draper",..: 1 1 2 2 2 2 2 2 2 2 ...
..$ Name2: Factor w/ 39 levels "Abe Drexler",..: 15 31 2 4 5 6 8 9 11 21 ...
$ vertices:'data.frame': 45 obs. of 2 variables:
..$ label : Factor w/ 45 levels "Abe Drexler",..: 5 9 16 23 26 32 33 38 39 17 ...
..$ Gender: Factor w/ 2 levels "female","male": 1 2 2 1 2 1 2 2 2 2 ...
# Merge edges and vertices
mmnet <- merge(madmen$edges, madmen$vertices,
by.x = "Name1", by.y = "label",
all = TRUE)
# Examine structure of mmnet
str(mmnet)
'data.frame': 75 obs. of 3 variables:
$ Name1 : Factor w/ 45 levels "Betty Draper",..: 1 1 2 2 2 2 2 2 2 2 ...
$ Name2 : Factor w/ 39 levels "Abe Drexler",..: 15 31 2 4 5 6 8 9 11 21 ...
$ Gender: Factor w/ 2 levels "female","male": 1 1 2 2 2 2 2 2 2 2 ...
Build the network (2) Now that your data is in the correct format, you can build the actual network plot.
You’ll use the geom_net() function, a ggplot layer that’s in the geomnet package. The ggnetwork package is a popular alternative, but we will not discuss that here.
Can you finish the ggplot() command?
# geomnet is pre-loaded
# Merge edges and vertices
mmnet <- merge(madmen$edges, madmen$vertices,
by.x = "Name1", by.y = "label",
all = TRUE)
# Finish the ggplot command
ggplot(data = mmnet, aes(from_id = Name1, to_id = Name2)) +
geom_net(aes(col=Gender), size=6, linewidth=1, labelon=TRUE, fontsize=3, labelcolour="black")

Adjusting the network Let’s clean up the network a bit. As you can see, since this is in the ggplot2 framework, you can manually adjust the scales like you have always done.
Here you’re going to use another trick to remove all theme elements and make a clean network plot.
# geomnet is pre-loaded
library(ggmap)
Google Maps API Terms of Service: http://developers.google.com/maps/terms.
Please cite ggmap if you use it: see citation('ggmap') for details.
# Merge edges and vertices
mmnet <- merge(madmen$edges, madmen$vertices,
by.x = "Name1", by.y = "label",
all = TRUE)
# Tweak the network plot
ggplot(data = mmnet, aes(from_id = Name1, to_id = Name2)) +
geom_net(aes(col = Gender),
size = 6,
linewidth = 1,
labelon = TRUE,
fontsize = 3,
labelcolour = "black",
directed = TRUE) +
scale_color_manual(values = c("#FF69B4", "#0099ff")) +
xlim(c(-0.05, 1.05)) +
ggmap::theme_nothing(legend = TRUE) +
theme(legend.key = element_blank())
`panel.margin` is deprecated. Please use `panel.spacing` property instead

Autoplot on linear models R has several plotting methods for specific objects. For example using plot() on the results of an lm() call results in four plots that give you insight into how well the assigned model fits the data.
The ggfortify package is an all-purpose plot converter between base graphics and ggplot2 grid graphics.
You’ll explore exactly what we mean by graphics and grid in chapter 4. For now, just know that if you want to use the automatic output features in the context of ggplot2, they must first be converted to a ggplot object via ggfortify. This can be important at the superficial level, for consistency in appearance, but also at a deeper level, for later combining several plots in a single graphics device.
# Create linear model: res
res <- lm(Volume~Girth, data = trees)
# Plot res
plot(res)




# Import ggfortify and use autoplot()
library(ggfortify)
package 'ggfortify' was built under R version 3.4.4
autoplot(res, ncol=2)

ggfortify - time series Time series objects (class mts or ts) also have their own methods for plot(). ggfortify can also take advantage of this functionality.
In the workspace, you’ll find the variable Canada (it comes from the vars package): an mts class object with four series: prod is a measure of labour productivity, e is employment, U is the unemployment rate, and rw the real wage. They are each plotted as separate series by default.
# ggfortify and Canada are available
library(vars)
Loading required package: MASS
Attaching package: 'MASS'
The following object is masked _by_ '.GlobalEnv':
mammals
The following object is masked from 'package:dplyr':
select
Loading required package: strucchange
Loading required package: sandwich
Loading required package: urca
Loading required package: lmtest
package 'lmtest' was built under R version 3.4.4
# Inspect structure of Canada
str(Canada)
Time-Series [1:84, 1:4] from 1980 to 2001: 930 930 930 931 933 ...
- attr(*, "dimnames")=List of 2
..$ : NULL
..$ : chr [1:4] "e" "prod" "rw" "U"
# Call plot() on Canada
plot(Canada)

# Call autoplot() on Canada
autoplot(Canada)

Distance matrices and Multi-Dimensional Scaling (MDS) As you can probably imagine, distance matrices (class dist) contain the measured distance between all pair-wise combinations of many points. For example, the eurodist dataset contains the distances between major European cities. dist objects lend themselves well to autoplot().
The cmdscale() function from the stats package performs Classical Multi-Dimensional Scaling and returns point coodinates as a matrix. Although autoplot() will work on this object, it will produce a heatmap, and not a scatter plot. However, if either eig = TRUE, add = TRUE or x.ret = TRUE is specified, cmdscale() will return a list instead of matrix. In these cases, the list method for autoplot() in the ggfortify package can deal with the output. Specifics on multi-dimensional scaling is beyond the scope of this course, however details on the method and these arguments can be found in the help pages ?cmdscale.
# ggfortify and eurodist are available
# Autoplot + ggplot2 tweaking
autoplot(eurodist) +
coord_fixed()
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.

# Autoplot of MDS
autoplot(cmdscale(eurodist, eig = TRUE),
label = TRUE,
label.size = 3,
size = 0)

Plotting K-means clustering ggfortify also supports stats::kmeans class objects. You must explicitly pass the original data to the autoplot function via the data argument, since kmeans objects don’t contain the original data. The result will be automatically colored according to cluster.
Here, you’ll use the iris dataset and just look at K-means clustering, although this works on many clustering methods, including cluster::clara(), cluster::fanny(), cluster::pam() and stats::prcomp(). Unfortunately a discussion of these clustering methods is beyond the scope of this course.
# Perform clustering
iris_k <- kmeans(iris[-5], 3)
# Autoplot: color according to cluster
autoplot(iris_k, data = iris, frame = TRUE)

# Autoplot: above, plus shape according to species
autoplot(iris_k, data = iris, frame = TRUE, shape='Species')

Working with maps from the maps package: USA The easiest way to obtain map polygons is through the maps package. Unfortunately there are only a few locations available, but if your region of interest is included they are extremely convenient.
The available maps of political boundaries are:
Global: world, world2 Country: france, italy, nz, usa USA: county, state The maps can be accessed via ggplot2::map_data(), which converts the map into a data frame containing the variables long and lat. To draw the map, you need to use geom_polygon() which will connect the points of latitude and longitude for you.
library(maps)
package 'maps' was built under R version 3.4.4
# maps, ggplot2, and ggmap are pre-loaded
# Use map_data() to create usa and inspect
usa <- map_data("usa")
str(usa)
'data.frame': 7243 obs. of 6 variables:
$ long : num -101 -101 -101 -101 -101 ...
$ lat : num 29.7 29.7 29.7 29.6 29.6 ...
$ group : num 1 1 1 1 1 1 1 1 1 1 ...
$ order : int 1 2 3 4 5 6 7 8 9 10 ...
$ region : chr "main" "main" "main" "main" ...
$ subregion: chr NA NA NA NA ...
# Build the map
ggplot(usa, aes(x = long, y = lat, group = group)) +
geom_polygon() +
coord_map() +
theme_nothing()
`panel.margin` is deprecated. Please use `panel.spacing` property instead

The population pyramid Animations are particularly useful for temporal or geospatial data, and they are surprisingly easy to make! Here, you simply loop over the time variable in your dataset, composing a new plot for each subset in the data. These individual images are then cataloged in an animated GIF file.
To show this you’ll use a great animated population pyramid that was presented on the Revolutions blog. There are many more adjustments you could have made to the plot, but we’ll just make a barebones version here.
japan <- read.table("japanPOP.txt", header=TRUE)
head(japan)
# Inspect structure of japan
str(japan)
'data.frame': 8282 obs. of 4 variables:
$ AGE : int 0 1 2 3 4 5 6 7 8 9 ...
$ POP : int -572954 -581748 -585239 -582223 -568788 -571899 -590530 -602349 -612527 -620373 ...
$ time: int 2010 2010 2010 2010 2010 2010 2010 2010 2010 2010 ...
$ SEX : Factor w/ 2 levels "Female","Male": 2 2 2 2 2 2 2 2 2 2 ...
library(animation)
# Finish the code inside saveGIF
saveGIF({
# Loop through all time points
for (i in unique(japan$time)) {
# Subset japan: data
data <- subset(japan, time == i)
# Finish the ggplot command
p <- ggplot(data, aes(x = AGE, y = POP, fill = SEX, width = 1)) +
coord_flip() +
geom_bar(data = data[data$SEX == "Female",], stat = "identity") +
geom_bar(data = data[data$SEX == "Male",], stat = "identity") +
ggtitle(i)
print(p)
}
}, movie.name = "pyramid.gif", interval = 0.1)
sh: convert: command not found
Error in cmd.fun(sprintf("%s --version", convert), intern = TRUE) :
error in running command
[1] FALSE
Arranging plots (1) The functions in gridExtra allow you to arrange any number of plots in a variety of ways. Since you can access the legend as a separate object, that means you can also arrange multiple plots with a single legend, as shown in the viewer. This is a good alternative to faceting, since with facets it’s not possible to set a different geom for each sub-plot. Here, you can combine any variety of plots and use a consistent color scale with only one legend to unify the whole image.
To do this you’ll create a new arrange graphical object, using grid.arrange(), which will combine several pre-existing grobs. Just like with grid.rect() and rectGrob() there are two versions of the arrange grob, one grid.arrange() produces a graphics output, which means you just draw the item to the viewer, and arrangeGrob() which returns a graphical object, aka grob which can be further manipulated.
In this exercise, you’ll just create your objects and arrange them using grid.arrange(). In the first steps you created two basic plots, g1 and g2. In the next exercise you’ll see what to do about the legend.
# Add a theme (legend at the bottom)
g1 <- ggplot(mtcars, aes(wt, mpg, col = cyl)) +
geom_point(alpha = 0.5) +
theme(legend.position = "bottom")
# Add a theme (no legend)
g2 <- ggplot(mtcars, aes(disp, fill = cyl)) +
geom_histogram(position = "identity", alpha = 0.5, binwidth = 20) +
theme(legend.position = "none")
# Load gridExtra
library(gridExtra)
Attaching package: 'gridExtra'
The following objects are masked from 'package:ggtern':
arrangeGrob, grid.arrange
The following object is masked from 'package:dplyr':
combine
# Call grid.arrange()
grid.arrange(g1, g2, ncol = 2)

Arranging plots (2) In the previous exercise you did a bare-bones arrangement of plots, but it would be nicer if the plot looks like the one that’s shown in the viewer. You can imagine that you have three panels, not two. There are two asymmetrical rows, the small second row is where the legend is, and two symmetrical columns, where the plots are.
To obtain this plot you need to extract the legend. You already saw this in previous exercises and it has already been done for you; the legend is available as my_legend. Next you need to arrange all the items appropriately.
# ggplot2, grid and gridExtra have been loaded for you
# Definitions of g1 and g2
library("grid")
g1 <- ggplot(mtcars, aes(wt, mpg, col = cyl)) +
geom_point() +
theme(legend.position = "bottom")
g2 <- ggplot(mtcars, aes(disp, fill = cyl)) +
geom_histogram(binwidth = 20) +
theme(legend.position = "none")
legend_index <- 15
# Extract the legend from g1
my_legend <- ggplotGrob(g1)$grobs[[legend_index]]
# Create g1_noleg
g1_noleg <- g1 +
theme(legend.position = "none")
# Calculate the height: legend_height
legend_height <- sum(my_legend$heights)
# Arrange g1_noleg, g2 and my_legend
grid.arrange(g1_noleg, g2, my_legend,
layout_matrix = matrix(c(1, 3, 2, 3), ncol = 2),
heights = unit.c(unit(1, "npc") - legend_height, legend_height))


Base package bag plot Before you create your own stats layer, you’ll begin by understanding what a bag plot is, and how to get the data for your own plots.
For this you’ll use a fake dataset called test_data, which only contains two variables. A scatter plot is shown in the viewer.
The aplpack package, which contains the bagplot() and compute.bagplot() functions, has been loaded for you.
library(aplpack)
x = floor(runif(60, 1700, 3700 ))
y = floor(runif(60, 50, 330))
test_data <- data.frame("x" = x, "y" = y)
test_data
# Call bagplot() on test_data
bagplot(test_data)
# Call compute.bagplot on test_data, assign to bag
bag <- compute.bagplot(test_data)
# Display information
bag$hull.loop
x y
3673 176
3360 98
2808 66
2516 59
1705 70
1745 259
1773 307
1806 320
2698 307
3539 288
3591 283
3647 232
bag$hull.bag
[,1] [,2]
3448.683 201.23791
3435.747 198.10602
3341.618 175.55902
3119.893 139.79230
3007.928 125.57880
2890.010 110.68831
2727.063 96.41987
2596.434 85.44424
2532.538 81.69941
2484.876 78.91393
2390.600 81.80400
2342.252 86.06236
2291.073 94.43000
2285.795 95.37164
2055.984 136.86930
2031.283 142.80128
sl 1831.228 237.06798
1830.284 239.98771
1833.782 246.91343
1880.925 276.84057
1920.734 283.47928
1946.224 285.44842
2161.923 291.78007
2314.226 296.18919
2454.514 300.14002
2455.218 300.15760
2563.785 300.30527
2634.528 299.70904
2848.287 296.71008
2866.720 296.27750
2891.040 295.68152
2914.951 294.27776
2931.411 293.26476
3009.666 283.97861
sr 3368.374 238.53977
sr 3385.561 234.81173
sr 3473.191 209.94078
bag$pxy.outlier
NULL
# Highlight components
points(bag$hull.loop, col = "green", pch = 16)
points(bag$hull.bag, col = "orange", pch = 16)
points(bag$pxy.outlier, col = "purple", pch = 16)

Multilayer ggplot2 bag plot The viewer shows the plot you created in the last exercise.
With our current understanding, if we wanted to make a bag plot in ggplot2, we’d take the three data frames (for the loop, bag and outliers) and add them using three separate geom layers.
Let’s see how this simple solution works and in the next exercises you’ll expand on this topic to make a real stats layer.
The bag and test_data objects from the previous exercise are provided. test_data contains two variables: x and y.
# bag and test_data are available
# Create data frames from matrices
hull.loop <- data.frame(x = bag$hull.loop[,1], y = bag$hull.loop[,2])
hull.bag <- data.frame(x = bag$hull.bag[,1], y = bag$hull.bag[,2])
pxy.outlier <- data.frame(x = bag$pxy.outlier[,1], y = bag$pxy.outlier[,2])
# Finish the ggplot command
ggplot(test_data, aes(x = x, y = y)) +
geom_polygon(data = hull.loop, fill = "green") +
geom_polygon(data = hull.bag, fill = "orange") +
geom_point(data = pxy.outlier, col = "purple", pch = 16, cex = 1.5)

Creating ggproto functions Now that you know where to find the statistics and how to use them in ggplot2, let’s put them into the functions that will make them easier to use.
For this you’ll use the ggproto object-oriented programming system - the basis of creating a new layer in ggplot2. There are four arguments for a ggproto object. The first two arguments are its name and what it inherits from (in this case Stat). Next come the required aesthetics, and then, most importantly, what the stat should do. For each group of data it receives from the data layer, what should be computed? This will simply be the calculations you performed in the previous exercise.
The ggproto object definition of StatLoop is already provided. Can you finish the implementations for the other ones?
# ggproto for StatLoop (hull.loop)
StatLoop <- ggproto("StatLoop", Stat,
required_aes = c("x", "y"),
compute_group = function(data, scales) {
bag <- compute.bagplot(x = data$x, y = data$y)
data.frame(x = bag$hull.loop[,1], y = bag$hull.loop[,2])
})
# ggproto for StatBag (hull.bag)
StatBag <- ggproto("StatBag", Stat,
required_aes = c("x", "y"),
compute_group = function(data, scales) {
bag <- compute.bagplot(x = data$x, y = data$y)
data.frame(x = bag$hull.bag[,1], y = bag$hull.bag[,2])
})
# ggproto for StatOut (pxy.outlier)
StatOut <- ggproto("StatOut", Stat,
required_aes = c("x", "y"),
compute_group = function(data, scales) {
bag <- compute.bagplot(x = data$x, y = data$y)
data.frame(x = bag$pxy.outlier[,1], y = bag$pxy.outlier[,2])
})
Creating stat_bag() In the previous exercise you established three ggproto objects, now you need to combine them under a new ggplot2 function that you’ll call stat_bag().
Adding a stat_bag() layer will execute each of the three ggproto objects that you just created.
Your three objects are called StatLoop, StatBag, StatOut, so you’ll need three layers in your stat_bag() function, which you’ll make with the layer() function. When you have multiple layers, you can combine them in a list by simply calling list().
For each layer, you’ll also need to specify the approrpiate geom: “polygon” or “point”.
The framework for the stat_bag() layer function has been provided for you.
# StatLoop, StatBag and StatOut are available
# Combine ggproto objects in layers to build stat_bag()
stat_bag <- function(mapping = NULL, data = NULL, geom = "polygon",
position = "identity", na.rm = FALSE, show.legend = NA,
inherit.aes = TRUE, loop = FALSE, ...) {
list(
# StatLoop layer
layer(
stat = StatLoop, data = data, mapping = mapping, geom = geom,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, alpha = 0.35, col = NA, ...)
),
# StatBag layer
layer(
stat = StatBag, data = data, mapping = mapping, geom = geom,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, alpha = 0.35, col = NA, ...)
),
# StatOut layer
layer(
stat = StatOut, data = data, mapping = mapping, geom = "point",
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, alpha = 0.7, col = NA, shape = 21, ...)
)
)
}
Use stat_bag() So far you’ve seen the basics for creating a new ggplot layer. It’s bare-bones, but functional. You now have a working solution to the bag plot question.
The ggplot2 command that you’ve coded before is available. now, let’s use stat_bag() to make our plot!
# hull.loop, hull.bag and pxy.outlier are available
# stat_bag, test_data and test_data2 are available
# Previous method
ggplot(test_data, aes(x = x, y = y)) +
geom_polygon(data = hull.loop, fill = "green") +
geom_polygon(data = hull.bag, fill = "orange") +
geom_point(data = pxy.outlier, col = "purple", pch = 16, cex = 1.5)

# stat_bag
ggplot(test_data, aes(x = x, y = y)) +
stat_bag(fill = 'black')

Viewport basics (1) To get familiar with grid graphics, you’ll begin with using some grid. functions. The grid package is already loaded into your R session, so you can get started straight away!
Note: In DataCamp’s learning interface, each change you make to the plot will appear as a new plot, so you can see the effect of each command.
# Draw rectangle in null viewport
grid.rect(gp = gpar(fill = "grey90"))
# Write text in null viewport
grid.text("null viewport")
# Draw a line
grid.lines(x = c(0, 0.75), y = c(0.25, 1),
gp = gpar(lty = 2, col = "red"))
Viewport basics (2) The code from the previous exercise that populates the null viewport with some basic shapes is already available. Let’s take the next step and start manipulating the stack of viewports.
# Populate null viewport
grid.rect(gp = gpar(fill = "grey90"))
grid.text("null viewport")
grid.lines(x = c(0,0.75), y = c(0.25, 1),
gp = gpar(lty = 2, col = "red"))
# Create new viewport: vp
vp <- viewport(x = 0.5, y = 0.5, width = 0.5, height = 0.5, just = "center")
# Push vp
pushViewport(vp)
# Populate new viewport with rectangle
grid.rect(gp = gpar(fill = "blue"))
Build a plot from scratch (1) Using the viewports, you can create plots, manipulating the space as needed.
In this exercise you’ll establish your grid viewport and in the following exercise you’ll populate it with values.
# 1 - Create plot viewport: pvp
mar <- c(5, 4, 2, 2)
pvp <- plotViewport(mar)
# 2 - Push pvp
pushViewport(pvp)
# 3 - Add rectangle
grid.rect(gp = gpar(fill = "grey80"))
# Create data viewport: dvp
dvp <- dataViewport(xData = mtcars$wt, yData = mtcars$mpg)
# 4 - Push dvp
pushViewport(dvp)
# Add two axes
grid.xaxis()
grid.yaxis()
Build a plot from scratch (2) The work you did before to build a plot from scratch is already included. Now you’re ready to add the points and the appropriate labels.
# Work from before
pushViewport(plotViewport(c(5, 4, 2, 2)))
grid.rect(gp = gpar())
pushViewport(dataViewport(xData = mtcars$wt, yData = mtcars$mpg))
grid.xaxis()
grid.yaxis()
# 1 - Add text to x axis
grid.text("Weight", y = unit(-3, "lines"))
# 2 - Add text to y axis
grid.text("MPG", x = unit(-3, "lines"), rot = 90)
# 3 - Add points
grid.points(x = mtcars$wt, y = mtcars$mpg, pch = 16)
Modifying a plot with grid.edit The commands you’ve coded up to now to create the plot are available in the editor. The great thing about grid, in comparison to base, is that you can name the different plot elements, so that you can access them and change them later on. You can do this with the grid.edit() function. Give it a try!
# Work from before
pushViewport(plotViewport(c(5, 4, 2, 2)))
grid.rect(gp = gpar())
pushViewport(dataViewport(xData = mtcars$wt, yData = mtcars$mpg))
grid.xaxis()
grid.yaxis()
# Work from before - add names
grid.text("Weight", y = unit(-3, "lines"), name = "xaxis")
grid.text("MPG", x = unit(-3, "lines"), rot = 90, name = "yaxis")
grid.points(x = mtcars$wt, y = mtcars$mpg, pch = 16, name = "datapoints")
# Edit "xaxis"
grid.edit("xaxis", label = "Weight (1000 lbs)")


# Edit "yaxis"
grid.edit("yaxis", label = "Miles/(US) gallon")

# Edit "datapoints"
grid.edit("datapoints", gp = (gpar(col = "#C3212766", cex = 2)))

Exploring the gTable In the previous chapter you saw graphical outputs using a variety of grid. functions. Graphical Objects, aka Grobs, are the object form of these items and can be found in your ggplot2 plots. Let’s take a look at how these grobs are stored in ggplot objects.
To start, a simple plot, p, has been coded for you.
# A simple plot p
p <- ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) + geom_point()
# Create gtab with ggplotGrob()
gtab <- ggplotGrob(p)
# Print out gtab
gtab
TableGrob (10 x 9) "layout": 18 grobs
z cells name grob
1 0 ( 1-10, 1- 9) background rect[plot.background..rect.8067]
2 5 ( 5- 5, 3- 3) spacer zeroGrob[NULL]
3 7 ( 6- 6, 3- 3) axis-l absoluteGrob[GRID.absoluteGrob.8043]
4 3 ( 7- 7, 3- 3) spacer zeroGrob[NULL]
5 6 ( 5- 5, 4- 4) axis-t zeroGrob[NULL]
6 1 ( 6- 6, 4- 4) panel gTree[panel-1.gTree.8023]
7 9 ( 7- 7, 4- 4) axis-b absoluteGrob[GRID.absoluteGrob.8036]
8 4 ( 5- 5, 5- 5) spacer zeroGrob[NULL]
9 8 ( 6- 6, 5- 5) axis-r zeroGrob[NULL]
10 2 ( 7- 7, 5- 5) spacer zeroGrob[NULL]
11 10 ( 4- 4, 4- 4) xlab-t zeroGrob[NULL]
12 11 ( 8- 8, 4- 4) xlab-b titleGrob[axis.title.x..titleGrob.8026]
13 12 ( 6- 6, 2- 2) ylab-l titleGrob[axis.title.y..titleGrob.8029]
14 13 ( 6- 6, 6- 6) ylab-r zeroGrob[NULL]
15 14 ( 6- 6, 8- 8) guide-box gtable[guide-box]
16 15 ( 3- 3, 4- 4) subtitle zeroGrob[plot.subtitle..zeroGrob.8064]
17 16 ( 2- 2, 4- 4) title zeroGrob[plot.title..zeroGrob.8063]
18 17 ( 9- 9, 4- 4) caption zeroGrob[plot.caption..zeroGrob.8065]
# Extract the grobs from gtab: gtab
g <- gtab$grobs
# Draw only the legend
legend_index <- which(vapply(g, inherits, what = "gtable", logical(1)))
grid.draw(g[[legend_index]])
Modifying the gTable You can visualize the layout of a gTable object with gtable_show_layout(). In the layout plot, each segment is labelled with its position.
The legend, that you can access with g[[8]], is a gTable itself, so you can also show its layout. It’s perfectly possible to update this layout by adding new graphical objects, similar to what you saw in the video.
library(gtable)
# Code from before
p <- ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) + geom_point()
gtab <- ggplotGrob(p)
g <- gtab$grobs
legend_index <- which(vapply(g, inherits, what = "gtable", logical(1)))
grid.draw(g[[legend_index]])
# 1 - Show layout of legend grob
gtable_show_layout(g[[legend_index]])

# Create text grob
my_text <- textGrob(label = "Motor Trend, 1974", gp = gpar(fontsize = 7, col = "gray25"))
# 2 - Use gtable_add_grob to modify original gtab
new_legend <- gtable_add_grob(gtab$grobs[[legend_index]], my_text, 3, 2)
# 3 - Update in gtab
gtab$grobs[[legend_index]] <- new_legend
# 4 - Draw gtab
grid.draw(gtab)

Exploring ggplot objects ggplot objects are basically just a named list that contains the information to make the actual plot. Here you’ll explore the structure of this object.
# Simple plot p
p <- ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) + geom_point()
# Examine class() and names()
class(p)
[1] "gg" "ggplot"
names(p)
[1] "data" "layers" "scales" "mapping" "theme" "coordinates" "facet" "plot_env"
[9] "labels"
# Print the scales sub-list
p$scales$scales
list()
# Update p
p <- p +
scale_x_continuous("Length", limits = c(4, 8), expand = c(0, 0)) +
scale_y_continuous("Width", limits = c(2, 4.5), expand = c(0, 0))
# Print the scales sub-list
p$scales$scales
[[1]]
<ScaleContinuousPosition>
Range:
Limits: 4 -- 8
[[2]]
<ScaleContinuousPosition>
Range:
Limits: 2 -- 4.5
ggplot_build and ggplot_gtable In the viewer we have produced a box plot of the mtcars dataset (called p) that you’ll use to explore two key ggplot functions for accessing the object internals: ggplot_build() and ggplot_gtable().
ggplot_build() is executed when you want to display or save an actual ggplot plot. It takes the data input and produces the visual output.
# Box plot of mtcars: p
p <- ggplot(mtcars, aes(x = factor(cyl), y = wt)) + geom_boxplot()
# Create pbuild
pbuild <- ggplot_build(p)
# a list of 3 elements
names(pbuild)
[1] "data" "layout" "plot"
# Print out each element in pbuild
pbuild$data
[[1]]
pbuild$panel
NULL
pbuild$plot
# Create gtab from pbuild
gtab <- ggplot_gtable(pbuild)
# Draw gtab
grid.draw(gtab)

Extracting details In the video you saw how to change the clipping parameters of a gTable object. Here, you’ll see something more practical: how to extract calculated values.
Many geoms are associated with underlying descriptive statistics which are calculated and then plotted. In these cases you actually don’t have the actual values that were plotted. Of course, these values are stored under the hood and you can access them in the results from ggplot_build(). This can be particularly useful for box plots. For example, since there are many methods for calculating Q1 and Q3, if you calculate your IQR and outliers outside of ggplot2 you may end up using a different method and get different results. Sometimes you want to have exactly the values that were plotted.
# Box plot of mtcars: p
p <- ggplot(mtcars, aes(x = factor(cyl), y = wt)) + geom_boxplot()
# Build pdata
pdata <- ggplot_build(p)$data
# confirm that the first element of the list is a data frame
class(pdata[[1]])
[1] "data.frame"
# Isolate this data frame
my_df <- pdata[[1]]
# The x labels
my_df$group <- c("4", "6", "8")
# Print out specific variables
my_df[c(1:6, 11)]
LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggZ2dwbG90IDMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClJlZnJlc2hlciAoMSkKQXMgYSByZWZyZXNoZXIgdG8gc3RhdGlzdGljYWwgcGxvdHMsIGxldCdzIGJ1aWxkIGEgc2NhdHRlciBwbG90IHdpdGggYW4gYWRkaXRpb25hbCBzdGF0aXN0aWMgbGF5ZXIuCgpBIGRhdGFzZXQgY2FsbGVkIG1vdmllc19zbWFsbCBpcyBjb2RlZCBpbiB5b3VyIHdvcmtzcGFjZS4gSXQgaXMgYSByYW5kb20gc2FtcGxlIG9mIDEwMDAgb2JzZXJ2YXRpb25zIGZyb20gdGhlIGxhcmdlciBtb3ZpZXMgZGF0YXNldCwgdGhhdCdzIGluc2lkZSB0aGUgZ2dwbG90Mm1vdmllcyBwYWNrYWdlLiBUaGUgZGF0YXNldCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiBtb3ZpZXMgZnJvbSBJTURCLiBUaGUgdmFyaWFibGUgdm90ZXMgaXMgdGhlIG51bWJlciBvZiBJTURCIHVzZXJzIHdobyBoYXZlIHJhdGVkIGEgbW92aWUgYW5kIHRoZSByYXRpbmcgKGNvbnZlcnRlZCBpbnRvIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUpIGlzIHRoZSBhdmVyYWdlIHJhdGluZyBmb3IgdGhlIG1vdmllLgoKYGBge3J9CiMgQ3JlYXRlIG1vdmllc19zbWFsbApsaWJyYXJ5KGdncGxvdDJtb3ZpZXMpCmxpYnJhcnkoZ2dwbG90MikKc2V0LnNlZWQoMTIzKQptb3ZpZXNfc21hbGwgPC0gbW92aWVzW3NhbXBsZShucm93KG1vdmllcyksIDEwMDApLCBdCm1vdmllc19zbWFsbCRyYXRpbmcgPC0gZmFjdG9yKHJvdW5kKG1vdmllc19zbWFsbCRyYXRpbmcpKQoKIyBFeHBsb3JlIG1vdmllc19zbWFsbCB3aXRoIHN0cigpCnN0cihtb3ZpZXNfc21hbGwpCgojIEJ1aWxkIGEgc2NhdHRlciBwbG90IHdpdGggbWVhbiBhbmQgOTUlIENJCmdncGxvdChtb3ZpZXNfc21hbGwsIGFlcyh4ID0gcmF0aW5nLCB5ID0gdm90ZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSAibWVhbl9jbF9ub3JtYWwiLAogICAgICAgICAgICAgICBnZW9tID0gImNyb3NzYmFyIiwKICAgICAgICAgICAgICAgd2lkdGggPSAwLjIsCiAgICAgICAgICAgICAgIGNvbCA9ICJyZWQiKSArCiAgc2NhbGVfeV9sb2cxMCgpCmBgYAoKUmVmcmVzaGVyICgyKQpUaGUgcGxvdCBpbiB0aGUgZ3JhcGhpY3MgZGV2aWNlIGlzIGEgdmFyaWF0aW9uIG9uIGFuIG9mdC1zZWVuIGdncGxvdDIgZXhhbXBsZSB1c2luZyB0aGUgZGlhbW9uZHMgZGF0YXNldCAoY29udGFpbmluZyBpbmZvcm1hdGlvbiBvbiBzZXZlcmFsIHZhcmlhYmxlcyBvZiBvdmVyIDUwLDAwMCBkaWFtb25kcykuCgpSZWNhbGwgdGhhdCB0aGVyZSBhcmUgYSB2YXJpZXR5IG9mIHNjYWxlXyBmdW5jdGlvbnMuIEhlcmUsIGRhdGEgYXJlIHRyYW5zZm9ybWVkIG9yIGZpbHRlcmVkIGZpcnN0LCBhZnRlciB3aGljaCB0aGUgcGxvdCBhbmQgYXNzb2NpYXRlZCBzdGF0aXN0aWNzIGFyZSBjb21wdXRlZC4gRm9yIGV4YW1wbGUsIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDEwMCwgMTAwMCkgd2lsbCByZW1vdmUgdmFsdWVzIG91dHNpZGUgdGhhdCByYW5nZS4KCkNvbnRyYXN0IHRoaXMgdG8gY29vcmRfY2FydGVzaWFuKCksIHdoaWNoIGNvbXB1dGVzIHRoZSBzdGF0aXN0aWNzIGJlZm9yZSBwbG90dGluZy4gVGhhdCBtZWFucyB0aGF0IHRoZSBwbG90IGFuZCBzdW1tYXJ5IHN0YXRpc3RpY3MgYXJlIHBlcmZvcm1lZCBvbiB0aGUgcmF3IGRhdGEuIFRoYXQncyB3aHkgd2Ugc2F5IHRoYXQgY29vcmRfY2FydGVzaWFuKGMoMTAwLCAxMDAwKSkgInpvb21zIGluIiBhIHBsb3QuIFRoaXMgd2FzIGRpc2N1c3NlZCBpbiB0aGUgY2hhcHRlciBvbiBjb29yZGluYXRlcyBpbiBjb3Vyc2UgMi4KCkhlcmUgd2UncmUgZ29pbmcgdG8gZXhwYW5kIG9uIHRoaXMgYW5kIGludHJvZHVjZSBzY2FsZV94X2xvZzEwKCkgYW5kIHNjYWxlX3lfbG9nMTAoKSB3aGljaCBwZXJmb3JtIGxvZzEwIHRyYW5zZm9ybWF0aW9ucywgYW5kIGNvb3JkX2VxdWFsKCksIHdoaWNoIHNldHMgYW4gYXNwZWN0IHJhdGlvIG9mIDEgKGNvb3JkX2ZpeGVkKCkgaXMgYWxzbyBhbiBvcHRpb24pLgoKWW91ciB0YXNrIGlzIHRvIHJlcHJvZHVjZSB0aGUgcGxvdCBpbiB0aGUgdmlld2VyLiBCZWZvcmUgeW91IGRvIHRoaXMsIGl0IG1pZ2h0IGJlIGEgZ29vZCBpZGVhIHRvIGV4cGxvcmUgZGlhbW9uZHMgaW4gdGhlIGNvbnNvbGUgaWYgeW91IGFyZSBub3QgZmFtaWxpYXIgd2l0aCBpdC4KCmBgYHtyfQojIFJlcHJvZHVjZSB0aGUgcGxvdApnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSwgY29sID0gY29sb3IpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuNSwgc2hhcGUgPSAxNikgKwogIHNjYWxlX3hfbG9nMTAoZXhwcmVzc2lvbihsb2dbMTBdKENhcmF0KSksIGxpbWl0cyA9IGMoMC4xLDEwKSkgKwogIHNjYWxlX3lfbG9nMTAoZXhwcmVzc2lvbihsb2dbMTBdKFByaWNlKSksIGxpbWl0cyA9IGMoMTAwLDEwMDAwMCkpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJZbE9yUmQiKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKUmVmcmVzaGVyICgzKQpUaGUgZ29hbCBwbG90IGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGlzIGNvZGVkIGluIHlvdXIgZWRpdG9yLiBIZXJlIHlvdSdsbCBleHBhbmQgb24gdGhpcyBwbG90IHdpdGggc3RhdF9zbW9vdGgoKSBtb2RlbCBpbnN0ZWFkIG9mIHNob3dpbmcgZXZlcnkgZGF0YSBwb2ludC4KCmBgYHtyfQojIEFkZCBzbW9vdGggbGF5ZXIgYW5kIGZhY2V0IHRoZSBwbG90CmdncGxvdChkaWFtb25kcywgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlLCBjb2wgPSBjb2xvcikpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgc2NhbGVfeF9sb2cxMChleHByZXNzaW9uKGxvZ1sxMF0oQ2FyYXQpKSwgbGltaXRzID0gYygwLjEsMTApKSArCiAgc2NhbGVfeV9sb2cxMChleHByZXNzaW9uKGxvZ1sxMF0oUHJpY2UpKSwgbGltaXRzID0gYygxMDAsMTAwMDAwKSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIllsT3JSZCIpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpUcmFuc2Zvcm1hdGlvbnMKSW4gdGhpcyBleGVyY2lzZSB5b3UnbGwgcmV0dXJuIHRvIHRoZSBmaXJzdCBwbG90dGluZyBleGVyY2lzZSBhbmQgc2VlIGhvdyBib3ggcGxvdHMgY29tcGFyZSB0byBkb3QgcGxvdHMgZm9yIHJlcHJlc2VudGluZyBoaWdoLWRlbnNpdHkgZGF0YS4KCkJveCBwbG90cyBhcmUgdmVyeSB1c2VmdWwsIGJ1dCB0aGV5IGRvbid0IHNvbHZlIGFsbCB5b3VyIHByb2JsZW1zIGFsbCB0aGUgdGltZSwgZm9yIGV4YW1wbGUsIHdoZW4geW91ciBkYXRhIGFyZSBoZWF2aWx5IHNrZXdlZCwgeW91IHdpbGwgc3RpbGwgbmVlZCB0byB0cmFuc2Zvcm0gaXQuIFlvdSdsbCBzZWUgdGhhdCBoZXJlLCB1c2luZyB0aGUgbW92aWVzX3NtYWxsIGRhdGFzZXQsIGEgc3Vic2V0IG9mIDEwLDAwMCBvYnNlcnZhdGlvbnMgb2YgZ2dwbG90Mm1vdmllczo6bW92aWVzLgoKYGBge3J9CiMgbW92aWVzX3NtYWxsIGlzIGF2YWlsYWJsZQoKIyBBZGQgYSBib3hwbG90IGdlb20KZCA8LSBnZ3Bsb3QobW92aWVzX3NtYWxsLCBhZXMoeCA9IHJhdGluZywgeSA9IHZvdGVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9ICJtZWFuX2NsX25vcm1hbCIsCiAgICAgICAgICAgICAgIGdlb20gPSAiY3Jvc3NiYXIiLAogICAgICAgICAgICAgICB3aWR0aCA9IDAuMiwKICAgICAgICAgICAgICAgY29sID0gInJlZCIpCgojIFVudHJhbnNmb3JtZWQgcGxvdApkCgojIFRyYW5zZm9ybSB0aGUgc2NhbGUKZCArIHNjYWxlX3lfbG9nMTAoKQoKIyBUcmFuc2Zvcm0gdGhlIGNvb3JkaW5hdGVzCmQgKyBjb29yZF90cmFucyh5ID0gImxvZzEwIikKYGBgCgpDdXQgaXQgdXAhCklmIHlvdSBvbmx5IGhhdmUgY29udGludW91cyB2YXJpYWJsZXMsIHlvdSBjYW4gY29udmVydCB0aGVtIGludG8gb3JkaW5hbCB2YXJpYWJsZXMgdXNpbmcgYW55IG9mIHRoZSBmb2xsb3dpbmcgZnVuY3Rpb25zOgoKY3V0X2ludGVydmFsKHgsIG4pIG1ha2VzIG4gZ3JvdXBzIGZyb20gdmVjdG9yIHggd2l0aCBlcXVhbCByYW5nZS4KY3V0X251bWJlcih4LCBuKSBtYWtlcyBuIGdyb3VwcyBmcm9tIHZlY3RvciB4IHdpdGggKGFwcHJveGltYXRlbHkpIGVxdWFsIG51bWJlcnMgb2Ygb2JzZXJ2YXRpb25zLgpjdXRfd2lkdGgoeCwgd2lkdGgpIG1ha2VzIGdyb3VwcyBvZiB3aWR0aCB3aWR0aCBmcm9tIHZlY3RvciB4LgpUaGlzIGlzIHVzZWZ1bCB3aGVuIHlvdSB3YW50IHRvIHN1bW1hcml6ZSBhIGNvbXBsZXggc2NhdHRlciBwbG90IGxpa2UgdGhlIG9uZSBzaG93biBpbiB0aGUgdmlld2VyLiBCeSBhcHBseWluZyB0aGVzZSBmdW5jdGlvbnMgdG8gdGhlIGNhcmF0IHZhcmlhYmxlIGFuZCBtYXBwaW5nIHRoYXQgb250byB0aGUgZ3JvdXAgYWVzdGhldGljLCB5b3UgY2FuIGNvbnZlcnQgdGhlIHNjYXR0ZXIgcGxvdCBpbiB0aGUgdmlld2VyIGludG8gYSBzZXJpZXMgb2YgYm94IHBsb3RzIG9uIHRoZSBmbHkuCgpgYGB7cn0KIyBQbG90IG9iamVjdCBwCnAgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKQoKIyBVc2UgY3V0X2ludGVydmFsCnAgKyBnZW9tX2JveHBsb3QoYWVzKGdyb3VwID0gY3V0X2ludGVydmFsKGNhcmF0LCBuPTEwKSkpCgojIFVzZSBjdXRfbnVtYmVyCnAgKyBnZW9tX2JveHBsb3QoYWVzKGdyb3VwID0gY3V0X251bWJlcihjYXJhdCwgbj0xMCkpKQoKIyBVc2UgY3V0X3dpZHRoCnAgKyBnZW9tX2JveHBsb3QoYWVzKGdyb3VwID0gY3V0X3dpZHRoKGNhcmF0LCB3aWR0aCA9IDAuMjUpKSkKYGBgCgpnZW9tX2RlbnNpdHkoKQpUbyBtYWtlIGEgc3RyYWlnaHRmb3J3YXJkIGRlbnNpdHkgcGxvdCwgYWRkIGEgZ2VvbV9kZW5zaXR5KCkgbGF5ZXIuCgpCZWZvcmUgcGxvdHRpbmcsIHlvdSB3aWxsIGNhbGN1bGF0ZSB0aGUgZW1wZXJpY2FsIGRlbnNpdHkgZnVuY3Rpb24sIHNpbWlsYXIgdG8gaG93IHlvdSBjYW4gdXNlIHRoZSBkZW5zaXR5KCkgZnVuY3Rpb24gaW4gdGhlIHN0YXRzIHBhY2thZ2UsIGF2YWlsYWJsZSBieSBkZWZhdWx0IHdoZW4geW91IHN0YXJ0IFIuIFRoZSBmb2xsb3dpbmcgZGVmYXVsdCBwYXJhbWV0ZXJzIGFyZSB1c2VkICh5b3UgY2FuIHNwZWNpZnkgdGhlc2UgYXJndW1lbnRzIGJvdGggaW4gZGVuc2l0eSgpIGFzIHdlbGwgYXMgZ2VvbV9kZW5zaXR5KCkpOgoKYncgPSAibnJkMCIsIHRlbGxpbmcgUiB3aGljaCBydWxlIHRvIHVzZSB0byBjaG9vc2UgYW4gYXBwcm9wcmlhdGUgYmFuZHdpZHRoLgprZXJuZWwgPSAiZ2F1c3NpYW4iLCB0ZWxsaW5nIFIgdG8gdXNlIHRoZSBHYXVzc2lhbiBrZXJuZWwuCldlJ3ZlIGFscmVhZHkgcHJlcGFyZWQgYSBkYXRhIGZyYW1lIHRlc3RfZGF0YSBmb3IgeW91LCBjb250YWluaW5nIHRocmVlIGNvbHVtbnM6IG5vcm0sIGJpbW9kYWwgYW5kIHVuaWZvcm0uIEVhY2ggY29sdW1uIHJlcHJlc2VudHMgMjAwIHNhbXBsZXMgZnJvbSBhIG5vcm1hbCwgYmltb2RhbCBhbmQgdW5pZm9ybSBkaXN0cmlidXRpb24uCgpgYGB7cn0Kcm4gPC0gcm5vcm0oMjAwLCAwLCAxKQoKYmltb2RhbERpc3RGdW5jIDwtIGZ1bmN0aW9uIChuLGNwY3QsIG11MSwgbXUyLCBzaWcxLCBzaWcyKSB7CiAgeTAgPC0gcmxub3JtKG4sbWVhbj1tdTEsIHNkID0gc2lnMSkKICB5MSA8LSBybG5vcm0obixtZWFuPW11Miwgc2QgPSBzaWcyKQoKICBmbGFnIDwtIHJiaW5vbShuLHNpemU9MSxwcm9iPWNwY3QpCiAgeSA8LSB5MCooMSAtIGZsYWcpICsgeTEqZmxhZyAKfQoKYm0gPC0gYmltb2RhbERpc3RGdW5jKG49MjAwLDAuNCwtMSwxLCAxLDEpCnVkIDwtIHJ1bmlmKDIwMCwgLTIsIDEpCnRlc3RfZGF0YSA8LSBkYXRhLmZyYW1lKCJub3JtIiA9IHJuLAogICAgICAgICAgICAgICAgICAgICAgICAiYmltb2RhbCIgPSBibSwKICAgICAgICAgICAgICAgICAgICAgICAgInVuaWZvcm0iID0gdWQpCmhlYWQodGVzdF9kYXRhKQoKYGBgCgpgYGB7cn0KIyB0ZXN0X2RhdGEgaXMgYXZhaWxhYmxlCgojIENhbGN1bGF0aW5nIGRlbnNpdHk6IGQKZCA8LSBkZW5zaXR5KHRlc3RfZGF0YSRub3JtKQoKIyBVc2Ugd2hpY2gubWF4KCkgdG8gY2FsY3VsYXRlIG1vZGUKbW9kZSA8LSBkJHhbd2hpY2gubWF4KGQkeSldCgojIEZpbmlzaCB0aGUgZ2dwbG90IGNhbGwKZ2dwbG90KHRlc3RfZGF0YSwgYWVzKHggPSBub3JtKSkgKwogIGdlb21fcnVnKCkgKwogIGdlb21fZGVuc2l0eSgpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtb2RlLCBjb2wgPSAicmVkIikKYGBgCgpDb21iaW5lIGRlbnNpdHkgcGxvdHMgYW5kIGhpc3RvZ3JhbQpTb21ldGltZXMgaXQgaXMgdXNlZnVsIHRvIGNvbXBhcmUgYSBoaXN0b2dyYW0gd2l0aCBhIGRlbnNpdHkgcGxvdC4gSG93ZXZlciwgdGhlIGhpc3RvZ3JhbSdzIHktc2NhbGUgbXVzdCBmaXJzdCBiZSBjb252ZXJ0ZWQgdG8gZnJlcXVlbmN5IGluc3RlYWQgb2YgYWJzb2x1dGUgY291bnQuIEFmdGVyIGRvaW5nIHNvLCB5b3UgY2FuIGFkZCBhbiBlbXBpcmljYWwgUERGIHVzaW5nIGdlb21fZGVuc2l0eSgpIG9yIGEgdGhlb3JldGljYWwgUERGIHVzaW5nIHN0YXRfZnVuY3Rpb24oKS4KCkNhbiB5b3UgZmluaXNoIHRoZSBwbG90IGJlbG93IGJ5IGZvbGxvd2luZyB0aGUgc3RlcHM/CgpgYGB7cn0KIyB0ZXN0X2RhdGEgaXMgYXZhaWxhYmxlCgojIEFyZ3VtZW50cyB5b3UnbGwgbmVlZCBsYXRlciBvbgpmdW5fYXJncyA8LSBsaXN0KG1lYW4gPSBtZWFuKHRlc3RfZGF0YSRub3JtKSwgc2QgPSBzZCh0ZXN0X2RhdGEkbm9ybSkpCgojIEZpbmlzaCB0aGUgZ2dwbG90CmdncGxvdCh0ZXN0X2RhdGEsIGFlcyh4ID0gbm9ybSkpICsKZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pKSsKZ2VvbV9kZW5zaXR5KGNvbCA9ICJyZWQiKSArCnN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBmdW5fYXJncywgY29sPSJibHVlIikKYGBgCgpBZGp1c3RpbmcgZGVuc2l0eSBwbG90cwpUaGVyZSBhcmUgdGhyZWUgcGFyYW1ldGVycyB0aGF0IHlvdSBtYXkgYmUgdGVtcHRlZCB0byBhZGp1c3QgaW4gYSBkZW5zaXR5IHBsb3Q6CgpidyAtIHRoZSBzbW9vdGhpbmcgYmFuZHdpZHRoIHRvIGJlIHVzZWQsIHNlZSA/ZGVuc2l0eSBmb3IgZGV0YWlscwphZGp1c3QgLSBhZGp1c3RtZW50IG9mIHRoZSBiYW5kd2lkdGgsIHNlZSBkZW5zaXR5IGZvciBkZXRhaWxzCmtlcm5lbCAtIGtlcm5lbCB1c2VkIGZvciBkZW5zaXR5IGVzdGltYXRpb24sIGRlZmluZWQgYXMKImciID0gZ2F1c3NpYW4KInIiID0gcmVjdGFuZ3VsYXIKInQiID0gdHJpYW5ndWxhcgoiZSIgPSBlcGFuZWNobmlrb3YKImIiID0gYml3ZWlnaHQKImMiID0gY29zaW5lCiJvIiA9IG9wdGNvc2luZQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSdsbCB1c2UgYSBkYXRhc2V0IGNvbnRhaW5pbmcgb25seSBmb3VyIHBvaW50cywgc21hbGxfZGF0YSwgc28gdGhhdCB5b3UgY2FuIHNlZSBob3cgdGhlc2UgdGhyZWUgYXJndW1lbnRzIGFmZmVjdCB0aGUgc2hhcGUgb2YgdGhlIGRlbnNpdHkgcGxvdC4KClRoZSB2ZWN0b3IgZ2V0X2J3IGNvbnRhaW5zIHRoZSBiYW5kd2lkdGggdGhhdCBpcyB1c2VkIGJ5IGRlZmF1bHQgaW4gZ2VvbV9kZW5zaXR5KCkuIHAgaXMgYSBiYXNpYyBwbG90dGluZyBvYmplY3QgdGhhdCB5b3UgY2FuIHN0YXJ0IGZyb20uCgpgYGB7cn0KIyBzbWFsbF9kYXRhIGlzIGF2YWlsYWJsZQpzbWFsbF9kYXRhIDwtIGRhdGEuZnJhbWUoIngiID0gYygtMy41LCAwLjAsMC41LCA2LjApKQoKIyBHZXQgdGhlIGJhbmR3aXRoCmdldF9idyA8LSBkZW5zaXR5KHNtYWxsX2RhdGEkeCkkYncKCiMgQmFzaWMgcGxvdHRpbmcgb2JqZWN0CnAgPC0gZ2dwbG90KHNtYWxsX2RhdGEsIGFlcyh4ID0geCkpICsKICBnZW9tX3J1ZygpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwwLjUpKQoKIyBDcmVhdGUgdGhyZWUgcGxvdHMKcCArIGdlb21fZGVuc2l0eSgpCnAgKyBnZW9tX2RlbnNpdHkoYWRqdXN0ID0gMC4yNSkKcCArIGdlb21fZGVuc2l0eShidyA9IDAuMjUgKiBnZXRfYncpCgojIENyZWF0ZSB0d28gcGxvdHMKcCArIGdlb21fZGVuc2l0eShrZXJuZWwgPSAiciIpCnAgKyBnZW9tX2RlbnNpdHkoa2VybmVsID0gImUiKQpgYGAKCkJveCBwbG90cyB3aXRoIHZhcnlpbmcgd2lkdGgKQSBkcmF3YmFjayBvZiBzaG93aW5nIGEgYm94IHBsb3QgcGVyIGdyb3VwLCBpcyB0aGF0IHlvdSBkb24ndCBoYXZlIGFueSBpbmRpY2F0aW9uIG9mIHRoZSBzYW1wbGUgc2l6ZSwgbiwgaW4gZWFjaCBncm91cCwgdGhhdCB3ZW50IGludG8gbWFraW5nIHRoZSBwbG90LiBPbmUgd2F5IG9mIGRlYWxpbmcgd2l0aCB0aGlzIGlzIHRvIHVzZSBhIHZhcmlhYmxlIHdpZHRoIGZvciB0aGUgYm94LCB3aGljaCByZWZsZWN0cyBkaWZmZXJlbmNlcyBpbiBuLgoKQ2FuIHlvdSBhZGQgc29tZSBnb29kLWxvb2tpbmcgYm94IHBsb3RzIHRvIHRoZSBiYXNpYyBwbG90IGNvZGVkIG9uIHRoZSByaWdodD8KCmBgYHtyfQojIEZpbmlzaCB0aGUgcGxvdApnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UsIGNvbCA9IGNvbG9yKSkgKwogIGdlb21fYm94cGxvdCh2YXJ3aWR0aCA9IFRSVUUpICsKICBmYWNldF9ncmlkKC4gfiBjb2xvcikKYGBgCgpNdWxpdHBsZSBkZW5zaXR5IHBsb3RzCkluIHRoaXMgZXhlcmNpc2UgeW91J2xsIGNvbWJpbmUgbXVsdGlwbGUgZGVuc2l0eSBwbG90cy4gSGVyZSwgeW91J2xsIGNvbWJpbmUganVzdCB0d28gZGlzdHJpYnV0aW9ucywgYSBub3JtYWwgYW5kIGEgYmltb2RhbC4KClRoZSBmaXJzdCB0aGluZyB0byByZW1lbWJlciBpcyB0aGF0IHlvdSBjYW4gY29uc2lkZXIgdmFsdWVzIGFzIHR3byBzZXBhcmF0ZSB2YXJpYWJsZXMsIGxpa2UgaW4gdGhlIHRlc3RfZGF0YSBkYXRhIGZyYW1lLCBvciBhcyBhIHNpbmdsZSBjb250aW51b3VzIHZhcmlhYmxlIHdpdGggdGhlaXIgSUQgYXMgYSBzZXBhcmF0ZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgbGlrZSBpbiB0aGUgdGVzdF9kYXRhMiBkYXRhIGZyYW1lLiB0ZXN0X2RhdGEyIGlzIG1vcmUgY29udmVuaWVudCBmb3IgY29tYmluaW5nIGFuZCBjb21wYXJpbmcgbXVsdGlwbGUgZGlzdHJpYnV0aW9ucy4KCmBgYHtyfQp0ZXN0X2RhdGEyIDwtIGRhdGEuZnJhbWUoImRpc3QiID0gYyhyZXAoIm5vcm0iLCAyMDApLCByZXAoImJpbW9kYWwiLCAyMDApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgPSBjKHRlc3RfZGF0YSRub3JtLCB0ZXN0X2RhdGEkYmltb2RhbCkpCgojIHRlc3RfZGF0YSBhbmQgdGVzdF9kYXRhMiBhcmUgYXZhaWxhYmxlCnN0cih0ZXN0X2RhdGEpCnN0cih0ZXN0X2RhdGEyKQoKIyBQbG90IHdpdGggdGVzdF9kYXRhCmdncGxvdCh0ZXN0X2RhdGEsIGFlcyh4ID0gbm9ybSkpICsKICBnZW9tX3J1ZygpKwogIGdlb21fZGVuc2l0eSgpCgojIFBsb3QgdHdvIGRpc3RyaWJ1dGlvbnMgd2l0aCB0ZXN0X2RhdGEyCmdncGxvdCh0ZXN0X2RhdGEyLCBhZXMoeCA9IHZhbHVlLCBmaWxsID0gZGlzdCwgY29sID0gZGlzdCkpICsKICBnZW9tX3J1ZyhhbHBoYSA9IDAuNikgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNikKCmBgYAoKTXVsdGlwbGUgZGVuc2l0eSBwbG90cyAoMikKV2hlbiB5b3UgbG9va2VkIGF0IG11bHRpcGxlIGJveCBwbG90cywgeW91IGNvbXBhcmVkIHRoZSB0b3RhbCBzbGVlcCB0aW1lIG9mIHZhcmlvdXMgbWFtbWFscywgc29ydGVkIGFjY29yZGluZyB0byB0aGVpciBlYXRpbmcgaGFiaXRzLiBPbmUgdGhpbmcgeW91IG5vdGVkIGlzIHRoYXQgZm9yIGluc2VjdGl2b3JlcywgYm94IHBsb3RzIGRpZG4ndCByZWFsbHkgbWFrZSBzZW5zZSwgc2luY2UgdGhlcmUgd2VyZSBvbmx5IDUgb2JzZXJ2YXRpb25zIHRvIGJlZ2luIHdpdGguIFlvdSBkZWNpZGVkIHRoYXQgeW91IGNvdWxkIG5vbmV0aGVsZXNzIHVzZSB0aGUgd2lkdGggb2YgYSBib3ggcGxvdCB0byBzaG93IHRoZSBkaWZmZXJlbmNlIGluIHNhbXBsZSBzaXplIGJldHdlZW4gdGhlIGdyb3Vwcy4gSGVyZSwgeW91J2xsIHNlZSBhIHNpbWlsYXIgdGhpbmcgd2l0aCBkZW5zaXR5IHBsb3RzLgoKQSBjbGVhbmVkIHVwIHZlcnNpb24gb2YgdGhlIG1hbW1hbGlhbiBkYXRhc2V0IGlzIGF2YWlsYWJsZSBhcyBtYW1tYWxzLgoKYGBge3J9CmhlYWQobXNsZWVwKQptYW1tYWxzIDwtIG1zbGVlcFssYygidm9yZSIsInNsZWVwX3RvdGFsIildCm1hbW1hbHMKYGBgCgpgYGB7cn0KIyBJbmRpdmlkdWFsIGRlbnNpdGllcwpnZ3Bsb3QobWFtbWFsc1ttYW1tYWxzJHZvcmUgPT0gIkluc2VjdGkiLCBdLCBhZXMoeCA9IHNsZWVwX3RvdGFsLCBmaWxsID0gdm9yZSkpICsKICBnZW9tX2RlbnNpdHkoY29sID0gTkEsIGFscGhhID0gMC4zNSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI0KSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjMpKQoKIyBXaXRoIGZhY2V0aW5nCmdncGxvdChtYW1tYWxzLCBhZXMoeCA9IHNsZWVwX3RvdGFsLCBmaWxsID0gdm9yZSkpICsKICBnZW9tX2RlbnNpdHkoY29sID0gTkEsIGFscGhhID0gMC4zNSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI0KSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjMpKSArCiAgZmFjZXRfd3JhcCggfiB2b3JlLCBucm93ID0gMikKCiMgTm90ZSB0aGF0IGJ5IGRlZmF1bHQsIHRoZSB4IHJhbmdlcyBmaWxsIHRoZSBzY2FsZQpnZ3Bsb3QobWFtbWFscywgYWVzKHggPSBzbGVlcF90b3RhbCwgZmlsbCA9IHZvcmUpKSArCiAgZ2VvbV9kZW5zaXR5KGNvbCA9IE5BLCBhbHBoYSA9IDAuMzUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyNCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMC4zKSkKCiMgVHJpbSBlYWNoIGRlbnNpdHkgcGxvdCBpbmRpdmlkdWFsbHkKZ2dwbG90KG1hbW1hbHMsIGFlcyh4ID0gc2xlZXBfdG90YWwsIGZpbGwgPSB2b3JlKSkgKwogIGdlb21fZGVuc2l0eShjb2wgPSBOQSwgYWxwaGEgPSAwLjM1LCB0cmltID0gVFJVRSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygwLDI0KSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjMpKQpgYGAKCldlaWdodGVkIGRlbnNpdHkgcGxvdHMKV2hlbiBwbG90dGluZyBhIHNpbmdsZSB2YXJpYWJsZSwgdGhlIGRlbnNpdHkgcGxvdHMgKGFuZCB0aGVpciBiYW5kd2lkdGhzKSBhcmUgY2FsY3VsYXRlZCBzZXBhcmF0ZSBmb3IgZWFjaCB2YXJpYWJsZSAoc2VlIHRoZSBwbG90IGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCBwcm92aWRlZCkuCgpIb3dldmVyLCB3aGVuIHlvdSBjb21wYXJlIHNldmVyYWwgdmFyaWFibGVzIChzdWNoIGFzIGVhdGluZyBoYWJpdHMpIGl0J3MgdXNlZnVsIHRvIHNlZSB0aGUgZGVuc2l0eSBvZiBlYWNoIHN1YnNldCBpbiByZWxhdGlvbiB0byB0aGUgd2hvbGUgZGF0YSBzZXQuIFRoaXMgaG9sZHMgdHJ1ZSBmb3IgbXVsdGlwbGUgZGVuc2l0eSBwbG90cyBhcyB3ZWxsIGFzIGZvciB2aW9saW4gcGxvdHMuCgpGb3IgdGhpcywgd2UgbmVlZCB0byB3ZWlnaHQgdGhlIGRlbnNpdHkgcGxvdHMgc28gdGhhdCB0aGV5J3JlIHJlbGF0aXZlIHRvIGVhY2ggb3RoZXIuIEVhY2ggZGVuc2l0eSBwbG90IGlzIGFkanVzdGVkIGFjY29yZGluZyB0byB3aGF0IHByb3BvcnRpb24gb2YgdGhlIHRvdGFsIGRhdGEgc2V0IGVhY2ggc3ViLWdyb3VwIHJlcHJlc2VudHMuIFdlIGNhbGN1bGF0ZWQgdGhpcyB1c2luZyB0aGUgZHBseXIgY29tbWFuZHMgb24gbGluZXMgMTEtMTUuCgpUaGUgbWFtbWFscyBkYXRhIGZyYW1lIGlzIGF2YWlsYWJsZSBhcyBiZWZvcmUuIEFmdGVyIGV4ZWN1dGluZyB0aGUgY29tbW5hZHMsIGl0IHdpbGwgaGF2ZSB0aGUgdmFyaWFibGUgbiwgd2hpY2ggd2UnbGwgdXNlIGZvciB3ZWlnaHRpbmcuCgpgYGB7cn0KIyBVbndlaWdodGVkIGRlbnNpdHkgcGxvdCBmcm9tIGJlZm9yZQpnZ3Bsb3QobWFtbWFscywgYWVzKHggPSBzbGVlcF90b3RhbCwgZmlsbCA9IHZvcmUpKSArCiAgZ2VvbV9kZW5zaXR5KGNvbCA9IE5BLCBhbHBoYSA9IDAuMzUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyNCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMC4zKSkKCiMgVW53ZWlnaHRlZCB2aW9saW4gcGxvdApnZ3Bsb3QobWFtbWFscywgYWVzKHggPSB2b3JlLCB5ID0gc2xlZXBfdG90YWwsIGZpbGwgPSB2b3JlKSkgKwogIGdlb21fdmlvbGluKCkKCiMgQ2FsY3VsYXRlIHdlaWdodGluZyBtZWFzdXJlCmxpYnJhcnkoZHBseXIpCm1hbW1hbHMyIDwtIG1hbW1hbHMgJT4lCiAgZ3JvdXBfYnkodm9yZSkgJT4lCiAgbXV0YXRlKG4gPSBuKCkgLyBucm93KG1hbW1hbHMpKSAtPiBtYW1tYWxzCgojIFdlaWdodGVkIGRlbnNpdHkgcGxvdApnZ3Bsb3QobWFtbWFscywgYWVzKHggPSBzbGVlcF90b3RhbCwgZmlsbCA9IHZvcmUpKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyh3ZWlnaHQgPSBuKSwgY29sID0gTkEsIGFscGhhID0gMC4zNSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI0KSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjMpKQoKIyBXZWlnaHRlZCB2aW9saW4gcGxvdApnZ3Bsb3QobWFtbWFscywgYWVzKHggPSB2b3JlLCB5ID0gc2xlZXBfdG90YWwsIGZpbGwgPSB2b3JlKSkgKwogIGdlb21fdmlvbGluKGFlcyh3ZWlnaHQgPSBuKSwgY29sID0gTkEpCmBgYAoKMkQgZGVuc2l0eSBwbG90cyAoMSkKWW91IGNhbiBjb25zaWRlciB0d28gb3J0aG9nb25hbCBkZW5zaXR5IHBsb3RzIGluIHRoZSBmb3JtIG9mIGEgMkQgZGVuc2l0eSBwbG90LiBKdXN0IGxpa2Ugd2l0aCBhIDFEIGRlbnNpdHkgcGxvdCwgeW91IGNhbiBhZGp1c3QgdGhlIGJhbmR3aWR0aCBvZiBib3RoIGF4ZXMgaW5kZXBlbmRlbnRseS4KClRoZSBkYXRhIGlzIHN0b3JlZCBpbiB0aGUgZmFpdGhmdWwgZGF0YSBmcmFtZSwgYXZhaWxhYmxlIGluIHRoZSBkYXRhc2V0cyBwYWNrYWdlLiBUaGUgb2JqZWN0IHAgY29udGFpbnMgdGhlIGJhc2UgZGVmaW5pdGlvbnMgb2YgYSBwbG90LgoKYGBge3J9CiMgQmFzZSBsYXllcnMKcCA8LSBnZ3Bsb3QoZmFpdGhmdWwsIGFlcyh4ID0gd2FpdGluZywgeSA9IGVydXB0aW9ucykpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLCA1LjUpLCBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoNDAsIDEwMCksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBjb29yZF9maXhlZCg2MCAvIDQuNSkKCiMgMSAtIFVzZSBnZW9tX2RlbnNpdHlfMmQoKQpwICsgZ2VvbV9kZW5zaXR5XzJkKCkKCiMgMiAtIFVzZSBzdGF0X2RlbnNpdHlfMmQoKSB3aXRoIGFyZ3VtZW50cwpwICsgc3RhdF9kZW5zaXR5XzJkKGFlcyhjb2wgPSAuLmxldmVsLi4pLCBoID0gYyg1LCAwLjUpKQpgYGAKCjJEIGRlbnNpdHkgcGxvdHMgKDIpCkNvbnRpbnVpbmcgd2l0aCB0aGUgZGVuc2l0eSBwbG90cyBmcm9tIHRoZSBsYXN0IGV4ZXJjaXNlLCBoZXJlIHlvdSdsbCBleHBsb3JlIHRoZSB2aXJpZGlzIHBhY2thZ2UuIFRoaXMgcGFja2FnZSBjb250YWlucyBtdWx0aS1odWUgY29sb3IgcGFsZXR0ZXMgc3VpdGFibGUgZm9yIGNvbnRpbnVvdXMgdmFyaWFibGVzLgoKVGhlIGFkdmFudGFnZSBvZiB0aGVzZSBzY2FsZXMgaXMgdGhhdCBpbnN0ZWFkIG9mIHByb3ZpZGluZyBhbiBldmVuIGNvbG9yIGdyYWRpZW50IGZvciBhIGNvbnRpbnVvdXMgc2NhbGUsIHRoZXkgaGlnaGxpZ2h0IHRoZSBoaWdoZXN0IHZhbHVlcyBieSB1c2luZyBhbiB1bmV2ZW4gY29sb3IgZ3JhZGllbnQgb24gcHVycG9zZS4gVGhlIGhpZ2ggdmFsdWVzIGFyZSBsaWdodGVyIGNvbG9ycyAoeWVsbG93IHZlcnN1cyBibHVlKSwgc28gdGhleSBzdGFuZCBvdXQgbW9yZS4KCkEgc2hhZGVkIDJEIGRlbnNpdHkgcGxvdCBzaG93aW5nIHRoZSBzYW1lIGRhdGEgYXMgdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGhhcyBiZWVuIHByb3ZpZGVkIGZvciB5b3UuIFVwIHRvIHlvdSB0byB1cGdyYWRlIGl0IQoKYGBge3J9CiMgTG9hZCBpbiB0aGUgdmlyaWRpcyBwYWNrYWdlCmxpYnJhcnkodmlyaWRpcykKCiMgQWRkIHZpcmlkaXMgY29sb3Igc2NhbGUKZ2dwbG90KGZhaXRoZnVsLCBhZXMoeCA9IHdhaXRpbmcsIHkgPSBlcnVwdGlvbnMpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwgNS41KSwgZXhwYW5kID0gYygwLDApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoNDAsIDEwMCksIGV4cGFuZCA9IGMoMCwwKSkgKwogIGNvb3JkX2ZpeGVkKDYwLzQuNSkgKwogIHN0YXRfZGVuc2l0eV8yZChnZW9tID0gInRpbGUiLCBhZXMoZmlsbCA9IC4uZGVuc2l0eS4uKSwgaD1jKDUsLjUpLCBjb250b3VyID0gRkFMU0UpKyBzY2FsZV9maWxsX3ZpcmlkaXMoKQpgYGAKClBhaXIgcGxvdHMgYW5kIGNvcnJlbGF0aW9uIG1hdHJpY2VzCk9uIHN0YXJ0dXAsIFIgZmVhdHVyZXMgdHdvIHVzZWZ1bCBxdWljay1hbmQtZGlydHkgcGFpcnMgcGxvdHMgZnVuY3Rpb25zLiBUaGV5IGJvdGggb25seSB0YWtlIGNvbnRpbnVvdXMgdmFyaWFibGVzLgoKWW91J2xsIGJlIHdvcmtpbmcgd2l0aCB0aGUgaXJpcyBkYXRhc2V0IGFuZCB3aXRoIG10Y2Fyc19mYWN0LCBhIHZlcnNpb24gb2YgbXRjYXJzIHdoZXJlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBoYXZlIGJlZW4gY29udmVydGVkIGludG8gYWN0dWFsIGZhY3RvciBjb2x1bW5zLgoKYGBge3J9CiMgcGFpcnMKcGFpcnMoaXJpc1sxOjRdKQoKIyBjaGFydC5Db3JyZWxhdGlvbgpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQpjaGFydC5Db3JyZWxhdGlvbihpcmlzWzE6NF0pCgojIGdncGFpcnMKbGlicmFyeShHR2FsbHkpCmdncGFpcnMoaXJpc1sxOjNdKQpgYGAKCkNyZWF0ZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCBpbiBnZ3Bsb3QyCkluc3RlYWQgb2YgdXNpbmcgYW4gb2ZmLXRoZS1zaGVsZiBjb3JyZWxhdGlvbiBtYXRyaXggZnVuY3Rpb24sIHlvdSBjYW4gb2YgY291cnNlIGNyZWF0ZSB5b3VyIG93biBwbG90LiBKdXN0IGZvciBmdW4sIGluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCByZS1jcmVhdGUgdGhlIHNjYXR0ZXJwbG90IHlvdSBzZWUgb24gdGhlIHJpZ2h0LiBUaGUgc3RyZW5ndGggb2YgdGhlIGNvcnJlbGF0aW9uIGlzIGRlcGljdGVkIGJ5IHRoZSBzaXplIGFuZCBjb2xvciBvZiB0aGUgcG9pbnRzIGFuZCBsYWJlbHMuCgpGb3Igc3RhcnRlcnMsIGEgY29ycmVsYXRpb24gbWF0cml4IGNhbiBiZSBjYWxjdWxhdGVkIHVzaW5nLCBmb3IgZXhhbXBsZSwgY29yKGRhdGFmcmFtZSkgKGlmIGFsbCB2YXJpYWJsZXMgYXJlIG51bWVyaWNhbCkuIEJlZm9yZSB5b3UgY2FuIHVzZSB5b3VyIGRhdGEgZnJhbWUgdG8gY3JlYXRlIHlvdXIgb3duIGNvcnJlbGF0aW9uIG1hdHJpeCBwbG90LCB5b3UnbGwgbmVlZCB0byBnZXQgaXQgaW4gdGhlIHJpZ2h0IGZvcm1hdC4KCkluIHRoZSBlZGl0b3IsIHlvdSBjYW4gc2VlIHRoZSBkZWZpbml0aW9uIG9mIGNvcl9saXN0KCksIGEgZnVuY3Rpb24gdGhhdCByZS1mb3JtYXRzIHRoZSBkYXRhIGZyYW1lIHguIEhlcmUsIEwgaXMgdXNlZCB0byBhZGQgdGhlIHBvaW50cyB0byB0aGUgbG93ZXIgdHJpYW5nbGUgb2YgdGhlIG1hdHJpeCwgYW5kIE0gaXMgdXNlZCB0byBhZGQgdGhlIG51bWVyaWNhbCB2YWx1ZXMgYXMgdGV4dCB0byB0aGUgdXBwZXIgdHJpYW5nbGUgb2YgdGhlIG1hdHJpeC4gV2l0aCByZXNoYXBlMjo6bWVsdCgpLCB0aGUgY29ycmVsYXRpb24gbWF0cmljZXMgTCBhbmQgTSBhcmUgZWFjaCBjb252ZXJ0ZWQgaW50byBhIHRocmVlLWNvbHVtbiBkYXRhIGZyYW1lOiB0aGUgeCBhbmQgeSBheGVzIG9mIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggbWFrZSB1cCB0aGUgZmlyc3QgdHdvIGNvbHVtbnMgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG1ha2VzIHVwIHRoZSB0aGlyZCBjb2x1bW4uIFRoZXNlIGJlY29tZSB0aGUgbmV3IHZhcmlhYmxlcyAicG9pbnRzIiBhbmQgImxhYmVscyIsIHdoaWNoIGNhbiBiZSBtYXBwZWQgb250byB0aGUgc2l6ZSBhZXN0aGV0aWMgZm9yIHRoZSBwb2ludHMgaW4gdGhlIGxvd2VyIHRyaWFuZ2xlIGFuZCBvbnRvIHRoZSBsYWJlbCBhZXN0aGV0aWMgZm9yIHRoZSB0ZXh0IGluIHRoZSB1cHBlciB0cmlhbmdsZSwgcmVzcGVjdGl2ZWx5LiBUaGVpciB2YWx1ZXMgd2lsbCBiZSB0aGUgc2FtZSwgYnV0IHRoZWlyIHBvc2l0aW9ucyBvbiB0aGUgcGxvdCB3aWxsIGJlIHN5bW1ldHJpY2FsIGFib3V0IHRoZSBkaWFnb25hbCEgTWVyZ2luZyBMIGFuZCBNLCB5b3UgaGF2ZSBldmVyeXRoaW5nIHlvdSBuZWVkLgoKSWYgeW91J3JlIG5vdCBmYW1pbGlhciB3aXRoIHJlc2hhcGUyIC0gZG9uJ3Qgd29ycnksIHRoZSBvbmx5IHJlYXNvbiB3ZSB1c2UgdGhhdCBpbnN0ZWFkIG9mIHRpZHlyIGlzIHRoYXQgcmVzaGFwZTI6Om1lbHQoKSBjYW4gaGFuZGxlIGEgbWF0cml4LCB3aGVyZWFzIHRpZHlyOjpnYXRoZXIoKSByZXF1aXJlcyBhIGRhdGEgZnJhbWUuIEF0IHRoaXMgcG9pbnQgeW91IGp1c3QgbmVlZCB0byB1bmRlcnN0YW5kIGhvdyB0byB1c2UgdGhlIG91dHB1dCBmcm9tIGNvcl9saXN0KCkuCgpZb3UnbGwgZmlyc3QgdXNlIGRwbHlyIHRvIGV4ZWN1dGUgdGhpcyBmdW5jdGlvbiBvbiB0aGUgY29udGludW91cyB2YXJpYWJsZXMgaW4gdGhlIGlyaXMgZGF0YSBmcmFtZSAodGhlIGZpcnN0IGZvdXIgY29sdW1ucyksIGJ1dCBzZXBhcmF0ZWx5IGZvciBlYWNoIHNwZWNpZXMuIFBsZWFzZSByZWZlciB0byB0aGUgY291cnNlIG9uIGRwbHlyIGlmIHlvdSBhcmUgbm90IGZhbWlsaWFyIHdpdGggdGhlc2UgZnVuY3Rpb25zLgoKTmV4dCwgeW91J2xsIGFjdHVhbGx5IHBsb3QgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHdpdGggZ2dwbG90MiBmdW5jdGlvbnMuCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQoKY29yX2xpc3QgPC0gZnVuY3Rpb24oeCkgewogIEwgPC0gTSA8LSBjb3IoeCkKICAKICBNW2xvd2VyLnRyaShNLCBkaWFnID0gVFJVRSldIDwtIE5BCiAgTSA8LSBtZWx0KE0pCiAgbmFtZXMoTSlbM10gPC0gInBvaW50cyIKICAKICBMW3VwcGVyLnRyaShMLCBkaWFnID0gVFJVRSldIDwtIE5BCiAgTCA8LSBtZWx0KEwpCiAgbmFtZXMoTClbM10gPC0gImxhYmVscyIKICAKICBtZXJnZShNLCBMKQp9CgojIENhbGN1bGF0ZSB4eCB3aXRoIGNvcl9saXN0CmxpYnJhcnkoZHBseXIpCnh4IDwtIGlyaXMgJT4lCiAgZ3JvdXBfYnkoU3BlY2llcykgJT4lCiAgZG8oY29yX2xpc3QoLlsxOjRdKSkgCgojIEZpbmlzaCB0aGUgcGxvdApnZ3Bsb3QoeHgsIGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sID0gcG9pbnRzLCBzaXplID0gYWJzKHBvaW50cykpLCBzaGFwZSA9IDE2KSArCiAgZ2VvbV90ZXh0KGFlcyhjb2wgPSBsYWJlbHMsICBzaXplID0gYWJzKGxhYmVscyksIGxhYmVsID0gcm91bmQobGFiZWxzLCAyKSkpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygwLCA2KSkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50MigiciIsIGxpbWl0cyA9IGMoLTEsIDEpKSArCiAgc2NhbGVfeV9kaXNjcmV0ZSgiIiwgbGltaXRzID0gcmV2KGxldmVscyh4eCRWYXIxKSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKCIiKSArCiAgZ3VpZGVzKHNpemUgPSBGQUxTRSkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gLTEsIGludGVyY2VwdCA9IG5sZXZlbHMoeHgkVmFyMSkgKyAxKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZmFjZXRfZ3JpZCguIH4gU3BlY2llcykgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClByb3BvcnRpb25hbC9zdGFja2VkIGJhciBwbG90cwpCZWZvcmUgeW91IGhlYWQgb3ZlciB0byB0ZXJuYXJ5IHBsb3RzLCBsZXQncyB0cnkgdG8gbWFrZSBhIGNsYXNzaWNhbCBwcm9wb3J0aW9uYWwvc3RhY2tlZCBiYXIgcGxvdCBvZiBhIHN1YnNldCBvZiB0aGUgZGF0YS4gV2UnbGwgdXNlIGEgc3RhY2tlZCBiYXIgcGxvdCBhbmQgdGhlIGNvb3JkX2ZsaXAoKSBmdW5jdGlvbiB0byBmbGlwcyB0aGUgeCBhbmQgeSBheGVzLgoKVGhlIGRhdGEgZnJhbWUgZm9yIHRoZSBBZnJpY2FuIFNvaWwgUHJvZmlsZXMgRGF0YWJhc2UgaXMgYXZhaWxhYmxlIGluIHlvdXIgd29ya3NwYWNlIGFzIGFmcmljYSBhbmQgY2FuIGJlIGZvdW5kIGluIHRoZSBHU0lGIHBhY2thZ2UuIEl0IGNvbnRhaW5zIHRocmVlIGNvbHVtbnM6IFNhbmQsIFNpbHQgYW5kIENsYXkuIEEgc21hbGxlciB2ZXJzaW9uLCBjb250YWluaW5nIG9ubHkgNTAgb2JzZXJ2YXRpb25zIGlzIHN0b3JlZCBpbiBhZnJpY2Ffc2FtcGxlLgoKSW4gdGhlIGZpcnN0IGNvdXJzZSB3ZSBtZW50aW9uZWQgdGhhdCBpbiB0aGUgZGF0YSBsYXllciwgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBzaG91bGQgcmVmbGVjdCBob3cgeW91IHdpc2ggdG8gcGxvdCBpdC4gRm9yIGEgdGVybmFyeSBwbG90LCB5b3UgbmVlZCB0byBoYXZlIHRocmVlIHNlcGFyYXRlIHZhcmlhYmxlcywgZm9yIGV4YW1wbGUsIFNhbmQsIFNpbHQgYW5kIENsYXkgaW4gYWZyaWNhLiBIb3dldmVyLCBmb3IgYSBwcm9wb3J0aW9uYWwvc3RhY2tlZCBiYXIgcGxvdCwgeW91IGp1c3QgbmVlZCB0d28uIFRoZSB0eXBlIHNob3VsZCBiZSBkZWZpbmVkIGFzIHRocmVlIGxldmVscyB3aXRoaW4gYSBzaW5nbGUgZmFjdG9yIHZhcmlhYmxlLiBUaGF0IGlzLCB5b3Ugd2FudCB0aWR5IGRhdGEuCgpJdCdzIGFsc28gdXNlZnVsIHRvIG1haW50YWluIHRoZSBzaXRlIElEcyBhcyBhIHZhcmlhYmxlIHdpdGhpbiB0aGUgZGF0YSBmcmFtZSwgY3VycmVudGx5LCB0aGV5IGFyZSBzdG9yZWQgYXQgcm93IG5hbWVzLCB3aGljaCBpcyBwb29yIHN0eWxlIGFuZCBub3QgdXNlZnVsLgoKYGBge3J9CgojIEV4cGxvcmUgYWZyaWNhCnN0cihhZnJpY2EpCmFmcmljYV9zYW1wbGUgPC0gYWZyaWNhW3NhbXBsZShucm93KGFmcmljYSksIDUwKSwgXQpzdHIoYWZyaWNhX3NhbXBsZSkKCiMgQWRkIGFuIElEIGNvbHVtbiBmcm9tIHRoZSByb3cubmFtZXMKYWZyaWNhX3NhbXBsZSRJRCA8LSByb3cubmFtZXMoYWZyaWNhX3NhbXBsZSkKCiMgR2F0aGVyIGFmcmljYV9zYW1wbGUKbGlicmFyeSh0aWR5cikKYWZyaWNhX3NhbXBsZV90aWR5IDwtIGdhdGhlcihhZnJpY2Ffc2FtcGxlLCBrZXksIHZhbHVlLCAtSUQpCmhlYWQoYWZyaWNhX3NhbXBsZV90aWR5KQoKIyBGaW5pc2ggdGhlIGdncGxvdCBjb21tYW5kCmdncGxvdChhZnJpY2Ffc2FtcGxlX3RpZHksIGFlcyh4ID0gZmFjdG9yKElEKSwgeSA9IHZhbHVlLCBmaWxsID0ga2V5KSkgKwogIGdlb21fY29sKCkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKClByb2R1Y2luZyB0ZXJuYXJ5IHBsb3RzCk9rLCBsZXQncyBtb3ZlIG9udG8gdGVybmFyeSBwbG90cy4gRm9yIHRoaXMgeW91J2xsIHVzZSB0aGUgZ2d0ZXJuIHBhY2thZ2UsIHdoaWNoIHByb3ZpZGVzIHRoZSBnZ3Rlcm4oKSBmdW5jdGlvbi4KCkluIGNvbnRyYXN0IHRvIHdoYXQgeW91IGp1c3Qgc2F3IGluIGFmcmljYV9zbWFsbF90aWR5LCB0aGUgdGhyZWUgc29pbCBwcm9wZXJ0aWVzLCBTYW5kLCBTaWx0IGFuZCBDbGF5LCBhcmUgbm90IGdvaW5nIHRvIGJlIGxvY2F0ZWQgaW4gYSBzaW5nbGUgdmFyaWFibGUuIFRoZSBkaXN0aW5jdGlvbiBiZXR3ZWVuIHdpZGUgYW5kIHRpZHkgZm9ybWF0IGRhdGEgd2FzIGRpc2N1c3NlZCBpbiB0aGUgZmlyc3QgY291cnNlIGFuZCBoZXJlIHlvdSdsbCBzZWUgaXQgaW4gYWN0aW9uLiBTb21ldGltZXMgeW91IG5lZWQgdG8gcmVhcnJhbmdlIHlvdXIgZGF0YSBmb3IgdGhlIGRlc2lyZWQgcGxvdCB0eXBlLgoKSGVyZSwgeW91J2xsIHVzZSB0aGUgY29tcGxldGUgZGF0YXNldCwgYWZyaWNhLCBjb250YWluaW5nIHRocmVlIHNlcGFyYXRlIHZhcmlhYmxlcyBmb3IgdGhlIG1lYXN1cmVzIG9mIGludGVyZXN0OiB0aGF0IGZvcm1hdCBpcyBwZXJmZWN0IGZvciBhIHRlcm5hcnkgcGxvdC4KCmBgYHtyfQojIExvYWQgZ2d0ZXJuCmxpYnJhcnkoZ2d0ZXJuKQoKIyBCdWlsZCB0ZXJuYXJ5IHBsb3QKZ2d0ZXJuKGFmcmljYSwgYWVzKHggPSBTYW5kLCB5ID0gU2lsdCwgeiA9IENsYXkpKSArCiAgZ2VvbV9wb2ludChzaGFwZT0xNiwgYWxwaGE9MC4yKQpgYGAKCkFkanVzdGluZyB0ZXJuYXJ5IHBsb3RzClRlcm5hcnkgcGxvdHMgaGF2ZSBiZWVuIGFyb3VuZCBmb3IgYSB3aGlsZSBpbiBSOyB5b3UgY291bGQgYWNoaWV2ZSB0aGUgc2FtZSB0aGluZyB3aXRoIHRoZSB2Y2QgcGFja2FnZSBhdXRob3JlZCBieSBNaWNoYWVsIEZyaWVuZGx5LiBJZiB5b3UganVzdCBuZWVkIGEgcXVpY2sgYW5kIGRpcnR5IHRlcm5hcnkgcGxvdCwgdGhhdCBtYXkgc3VpdCB5b3UganVzdCBmaW5lLiBIb3dldmVyLCBzaW5jZSBnZ3Rlcm4gaXMgYnVpbHQgb24gZ2dwbG90MiwgeW91IGNhbiB0YWtlIGFkdmFudGFnZSBvZiBhbGwgdGhlIHRvb2xzIGF2YWlsYWJsZSB0aGVyZWluLgoKZ2d0ZXJuIGlzIGF1dGhvcmVkIGJ5IE5pY2hvbGFzIEhhbWlsdG9uLCBtb3JlIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBvbiBoaXMgcGFja2FnZSB3ZWJzaXRlOiB3d3cuZ2d0ZXJuLmNvbS4KClRoZSBwbG90IGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGlzIGF2YWlsYWJsZSB0d2ljZS4gQ2FuIHlvdSBhZGFwdCBpdCBpbiBkaWZmZXJlbnQgd2F5cyB0byBtYWtlIGRpZmZlcmVudCB0ZXJuYXJ5IGRlbnNpdHkgcGxvdHM/CgpgYGB7cn0KIyBnZ3Rlcm4gYW5kIGdncGxvdDIgYXJlIGxvYWRlZAojIE9yaWdpbmFsIHBsb3Q6CmdndGVybihhZnJpY2EsIGFlcyh4ID0gU2FuZCwgeSA9IFNpbHQsIHogPSBDbGF5KSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxNiwgYWxwaGEgPSAwLjIpCgojIFBsb3QgMQpnZ3Rlcm4oYWZyaWNhLCBhZXMoeCA9IFNhbmQsIHkgPSBTaWx0LCB6ID0gQ2xheSkpICsKICBnZW9tX2RlbnNpdHlfdGVybigpCgojIFBsb3QgMgpnZ3Rlcm4oYWZyaWNhLCBhZXMoeCA9IFNhbmQsIHkgPSBTaWx0LCB6ID0gQ2xheSkpICsKICBzdGF0X2RlbnNpdHlfdGVybihnZW9tID0gInBvbHlnb24iLCBhZXMoZmlsbCA9IC4ubGV2ZWwuLiwgYWxwaGEgPSAuLmxldmVsLi4pKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkKYGBgCgpCdWlsZCB0aGUgbmV0d29yayAoMSkKTmV0d29yayBkYXRhIG1heSBiZSBzdG9yZWQgaW4gYSB2YXJpZXR5IG9mIHdheXMuCgpGb3IgdGhpcyBleGFtcGxlLCB5b3UnbGwgdXNlIGFuIHVuZGlyZWN0ZWQgbmV0d29yayBvZiByb21hbnRpYyByZWxhdGlvbnNoaXBzIGluIHRoZSBUViBzaG93IE1hZCBNZW46IGdlb21uZXQ6Om1hZG1lbi4KCmBgYHtyfQojIExvYWQgZ2VvbW5ldCAmIGV4YW1pbmUgc3RydWN0dXJlIG9mIG1hZG1lbgpsaWJyYXJ5KGdlb21uZXQpCnN0cihtYWRtZW4pCgojIE1lcmdlIGVkZ2VzIGFuZCB2ZXJ0aWNlcwptbW5ldCA8LSBtZXJnZShtYWRtZW4kZWRnZXMsIG1hZG1lbiR2ZXJ0aWNlcywKICAgICAgICAgICAgICAgYnkueCA9ICJOYW1lMSIsIGJ5LnkgPSAibGFiZWwiLAogICAgICAgICAgICAgICBhbGwgPSBUUlVFKQoKIyBFeGFtaW5lIHN0cnVjdHVyZSBvZiBtbW5ldApzdHIobW1uZXQpCmBgYAoKQnVpbGQgdGhlIG5ldHdvcmsgKDIpCk5vdyB0aGF0IHlvdXIgZGF0YSBpcyBpbiB0aGUgY29ycmVjdCBmb3JtYXQsIHlvdSBjYW4gYnVpbGQgdGhlIGFjdHVhbCBuZXR3b3JrIHBsb3QuCgpZb3UnbGwgdXNlIHRoZSBnZW9tX25ldCgpIGZ1bmN0aW9uLCBhIGdncGxvdCBsYXllciB0aGF0J3MgaW4gdGhlIGdlb21uZXQgcGFja2FnZS4gVGhlIGdnbmV0d29yayBwYWNrYWdlIGlzIGEgcG9wdWxhciBhbHRlcm5hdGl2ZSwgYnV0IHdlIHdpbGwgbm90IGRpc2N1c3MgdGhhdCBoZXJlLgoKQ2FuIHlvdSBmaW5pc2ggdGhlIGdncGxvdCgpIGNvbW1hbmQ/CgpgYGB7cn0KIyBnZW9tbmV0IGlzIHByZS1sb2FkZWQKCiMgTWVyZ2UgZWRnZXMgYW5kIHZlcnRpY2VzCm1tbmV0IDwtIG1lcmdlKG1hZG1lbiRlZGdlcywgbWFkbWVuJHZlcnRpY2VzLAogICAgICAgICAgICAgICBieS54ID0gIk5hbWUxIiwgYnkueSA9ICJsYWJlbCIsCiAgICAgICAgICAgICAgIGFsbCA9IFRSVUUpCgojIEZpbmlzaCB0aGUgZ2dwbG90IGNvbW1hbmQKZ2dwbG90KGRhdGEgPSBtbW5ldCwgYWVzKGZyb21faWQgPSBOYW1lMSwgdG9faWQgPSBOYW1lMikpICsKICBnZW9tX25ldChhZXMoY29sPUdlbmRlciksIHNpemU9NiwgbGluZXdpZHRoPTEsIGxhYmVsb249VFJVRSwgZm9udHNpemU9MywgbGFiZWxjb2xvdXI9ImJsYWNrIikKYGBgCgpBZGp1c3RpbmcgdGhlIG5ldHdvcmsKTGV0J3MgY2xlYW4gdXAgdGhlIG5ldHdvcmsgYSBiaXQuIEFzIHlvdSBjYW4gc2VlLCBzaW5jZSB0aGlzIGlzIGluIHRoZSBnZ3Bsb3QyIGZyYW1ld29yaywgeW91IGNhbiBtYW51YWxseSBhZGp1c3QgdGhlIHNjYWxlcyBsaWtlIHlvdSBoYXZlIGFsd2F5cyBkb25lLgoKSGVyZSB5b3UncmUgZ29pbmcgdG8gdXNlIGFub3RoZXIgdHJpY2sgdG8gcmVtb3ZlIGFsbCB0aGVtZSBlbGVtZW50cyBhbmQgbWFrZSBhIGNsZWFuIG5ldHdvcmsgcGxvdC4KCmBgYHtyfQojIGdlb21uZXQgaXMgcHJlLWxvYWRlZApsaWJyYXJ5KGdnbWFwKQojIE1lcmdlIGVkZ2VzIGFuZCB2ZXJ0aWNlcwptbW5ldCA8LSBtZXJnZShtYWRtZW4kZWRnZXMsIG1hZG1lbiR2ZXJ0aWNlcywKICAgICAgICAgICAgICAgYnkueCA9ICJOYW1lMSIsIGJ5LnkgPSAibGFiZWwiLAogICAgICAgICAgICAgICBhbGwgPSBUUlVFKQoKIyBUd2VhayB0aGUgbmV0d29yayBwbG90CmdncGxvdChkYXRhID0gbW1uZXQsIGFlcyhmcm9tX2lkID0gTmFtZTEsIHRvX2lkID0gTmFtZTIpKSArCiAgZ2VvbV9uZXQoYWVzKGNvbCA9IEdlbmRlciksCiAgICAgICAgICAgc2l6ZSA9IDYsCiAgICAgICAgICAgbGluZXdpZHRoID0gMSwKICAgICAgICAgICBsYWJlbG9uID0gVFJVRSwKICAgICAgICAgICBmb250c2l6ZSA9IDMsCiAgICAgICAgICAgbGFiZWxjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgIGRpcmVjdGVkID0gVFJVRSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRkY2OUI0IiwgIiMwMDk5ZmYiKSkgKwogIHhsaW0oYygtMC4wNSwgMS4wNSkpICsKICBnZ21hcDo6dGhlbWVfbm90aGluZyhsZWdlbmQgPSBUUlVFKSArCiAgdGhlbWUobGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpBdXRvcGxvdCBvbiBsaW5lYXIgbW9kZWxzClIgaGFzIHNldmVyYWwgcGxvdHRpbmcgbWV0aG9kcyBmb3Igc3BlY2lmaWMgb2JqZWN0cy4gRm9yIGV4YW1wbGUgdXNpbmcgcGxvdCgpIG9uIHRoZSByZXN1bHRzIG9mIGFuIGxtKCkgY2FsbCByZXN1bHRzIGluIGZvdXIgcGxvdHMgdGhhdCBnaXZlIHlvdSBpbnNpZ2h0IGludG8gaG93IHdlbGwgdGhlIGFzc2lnbmVkIG1vZGVsIGZpdHMgdGhlIGRhdGEuCgpUaGUgZ2dmb3J0aWZ5IHBhY2thZ2UgaXMgYW4gYWxsLXB1cnBvc2UgcGxvdCBjb252ZXJ0ZXIgYmV0d2VlbiBiYXNlIGdyYXBoaWNzIGFuZCBnZ3Bsb3QyIGdyaWQgZ3JhcGhpY3MuCgpZb3UnbGwgZXhwbG9yZSBleGFjdGx5IHdoYXQgd2UgbWVhbiBieSBncmFwaGljcyBhbmQgZ3JpZCBpbiBjaGFwdGVyIDQuIEZvciBub3csIGp1c3Qga25vdyB0aGF0IGlmIHlvdSB3YW50IHRvIHVzZSB0aGUgYXV0b21hdGljIG91dHB1dCBmZWF0dXJlcyBpbiB0aGUgY29udGV4dCBvZiBnZ3Bsb3QyLCB0aGV5IG11c3QgZmlyc3QgYmUgY29udmVydGVkIHRvIGEgZ2dwbG90IG9iamVjdCB2aWEgZ2dmb3J0aWZ5LiBUaGlzIGNhbiBiZSBpbXBvcnRhbnQgYXQgdGhlIHN1cGVyZmljaWFsIGxldmVsLCBmb3IgY29uc2lzdGVuY3kgaW4gYXBwZWFyYW5jZSwgYnV0IGFsc28gYXQgYSBkZWVwZXIgbGV2ZWwsIGZvciBsYXRlciBjb21iaW5pbmcgc2V2ZXJhbCBwbG90cyBpbiBhIHNpbmdsZSBncmFwaGljcyBkZXZpY2UuCgpgYGB7cn0KIyBDcmVhdGUgbGluZWFyIG1vZGVsOiByZXMKcmVzIDwtIGxtKFZvbHVtZX5HaXJ0aCwgZGF0YSA9IHRyZWVzKQoKIyBQbG90IHJlcwpwbG90KHJlcykKCiMgSW1wb3J0IGdnZm9ydGlmeSBhbmQgdXNlIGF1dG9wbG90KCkKbGlicmFyeShnZ2ZvcnRpZnkpCmF1dG9wbG90KHJlcywgbmNvbD0yKQpgYGAKCmdnZm9ydGlmeSAtIHRpbWUgc2VyaWVzClRpbWUgc2VyaWVzIG9iamVjdHMgKGNsYXNzIG10cyBvciB0cykgYWxzbyBoYXZlIHRoZWlyIG93biBtZXRob2RzIGZvciBwbG90KCkuIGdnZm9ydGlmeSBjYW4gYWxzbyB0YWtlIGFkdmFudGFnZSBvZiB0aGlzIGZ1bmN0aW9uYWxpdHkuCgpJbiB0aGUgd29ya3NwYWNlLCB5b3UnbGwgZmluZCB0aGUgdmFyaWFibGUgQ2FuYWRhIChpdCBjb21lcyBmcm9tIHRoZSB2YXJzIHBhY2thZ2UpOiBhbiBtdHMgY2xhc3Mgb2JqZWN0IHdpdGggZm91ciBzZXJpZXM6IHByb2QgaXMgYSBtZWFzdXJlIG9mIGxhYm91ciBwcm9kdWN0aXZpdHksIGUgaXMgZW1wbG95bWVudCwgVSBpcyB0aGUgdW5lbXBsb3ltZW50IHJhdGUsIGFuZCBydyB0aGUgcmVhbCB3YWdlLiBUaGV5IGFyZSBlYWNoIHBsb3R0ZWQgYXMgc2VwYXJhdGUgc2VyaWVzIGJ5IGRlZmF1bHQuCgpgYGB7cn0KIyBnZ2ZvcnRpZnkgYW5kIENhbmFkYSBhcmUgYXZhaWxhYmxlCmxpYnJhcnkodmFycykKIyBJbnNwZWN0IHN0cnVjdHVyZSBvZiBDYW5hZGEKc3RyKENhbmFkYSkKCiMgQ2FsbCBwbG90KCkgb24gQ2FuYWRhCnBsb3QoQ2FuYWRhKQoKIyBDYWxsIGF1dG9wbG90KCkgb24gQ2FuYWRhCmF1dG9wbG90KENhbmFkYSkKCmBgYAoKRGlzdGFuY2UgbWF0cmljZXMgYW5kIE11bHRpLURpbWVuc2lvbmFsIFNjYWxpbmcgKE1EUykKQXMgeW91IGNhbiBwcm9iYWJseSBpbWFnaW5lLCBkaXN0YW5jZSBtYXRyaWNlcyAoY2xhc3MgZGlzdCkgY29udGFpbiB0aGUgbWVhc3VyZWQgZGlzdGFuY2UgYmV0d2VlbiBhbGwgcGFpci13aXNlIGNvbWJpbmF0aW9ucyBvZiBtYW55IHBvaW50cy4gRm9yIGV4YW1wbGUsIHRoZSBldXJvZGlzdCBkYXRhc2V0IGNvbnRhaW5zIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiBtYWpvciBFdXJvcGVhbiBjaXRpZXMuIGRpc3Qgb2JqZWN0cyBsZW5kIHRoZW1zZWx2ZXMgd2VsbCB0byBhdXRvcGxvdCgpLgoKVGhlIGNtZHNjYWxlKCkgZnVuY3Rpb24gZnJvbSB0aGUgc3RhdHMgcGFja2FnZSBwZXJmb3JtcyBDbGFzc2ljYWwgTXVsdGktRGltZW5zaW9uYWwgU2NhbGluZyBhbmQgcmV0dXJucyBwb2ludCBjb29kaW5hdGVzIGFzIGEgbWF0cml4LiBBbHRob3VnaCBhdXRvcGxvdCgpIHdpbGwgd29yayBvbiB0aGlzIG9iamVjdCwgaXQgd2lsbCBwcm9kdWNlIGEgaGVhdG1hcCwgYW5kIG5vdCBhIHNjYXR0ZXIgcGxvdC4gSG93ZXZlciwgaWYgZWl0aGVyIGVpZyA9IFRSVUUsIGFkZCA9IFRSVUUgb3IgeC5yZXQgPSBUUlVFIGlzIHNwZWNpZmllZCwgY21kc2NhbGUoKSB3aWxsIHJldHVybiBhIGxpc3QgaW5zdGVhZCBvZiBtYXRyaXguIEluIHRoZXNlIGNhc2VzLCB0aGUgbGlzdCBtZXRob2QgZm9yIGF1dG9wbG90KCkgaW4gdGhlIGdnZm9ydGlmeSBwYWNrYWdlIGNhbiBkZWFsIHdpdGggdGhlIG91dHB1dC4gU3BlY2lmaWNzIG9uIG11bHRpLWRpbWVuc2lvbmFsIHNjYWxpbmcgaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIGNvdXJzZSwgaG93ZXZlciBkZXRhaWxzIG9uIHRoZSBtZXRob2QgYW5kIHRoZXNlIGFyZ3VtZW50cyBjYW4gYmUgZm91bmQgaW4gdGhlIGhlbHAgcGFnZXMgP2NtZHNjYWxlLgoKYGBge3J9CiMgZ2dmb3J0aWZ5IGFuZCBldXJvZGlzdCBhcmUgYXZhaWxhYmxlCiMgQXV0b3Bsb3QgKyBnZ3Bsb3QyIHR3ZWFraW5nCmF1dG9wbG90KGV1cm9kaXN0KSArIAogIGNvb3JkX2ZpeGVkKCkKCiMgQXV0b3Bsb3Qgb2YgTURTCmF1dG9wbG90KGNtZHNjYWxlKGV1cm9kaXN0LCBlaWcgPSBUUlVFKSwgCiAgICAgICAgIGxhYmVsID0gVFJVRSwgCiAgICAgICAgIGxhYmVsLnNpemUgPSAzLCAKICAgICAgICAgc2l6ZSA9IDApCmBgYAoKUGxvdHRpbmcgSy1tZWFucyBjbHVzdGVyaW5nCmdnZm9ydGlmeSBhbHNvIHN1cHBvcnRzIHN0YXRzOjprbWVhbnMgY2xhc3Mgb2JqZWN0cy4gWW91IG11c3QgZXhwbGljaXRseSBwYXNzIHRoZSBvcmlnaW5hbCBkYXRhIHRvIHRoZSBhdXRvcGxvdCBmdW5jdGlvbiB2aWEgdGhlIGRhdGEgYXJndW1lbnQsIHNpbmNlIGttZWFucyBvYmplY3RzIGRvbid0IGNvbnRhaW4gdGhlIG9yaWdpbmFsIGRhdGEuIFRoZSByZXN1bHQgd2lsbCBiZSBhdXRvbWF0aWNhbGx5IGNvbG9yZWQgYWNjb3JkaW5nIHRvIGNsdXN0ZXIuCgpIZXJlLCB5b3UnbGwgdXNlIHRoZSBpcmlzIGRhdGFzZXQgYW5kIGp1c3QgbG9vayBhdCBLLW1lYW5zIGNsdXN0ZXJpbmcsIGFsdGhvdWdoIHRoaXMgd29ya3Mgb24gbWFueSBjbHVzdGVyaW5nIG1ldGhvZHMsIGluY2x1ZGluZyBjbHVzdGVyOjpjbGFyYSgpLCBjbHVzdGVyOjpmYW5ueSgpLCBjbHVzdGVyOjpwYW0oKSBhbmQgc3RhdHM6OnByY29tcCgpLiBVbmZvcnR1bmF0ZWx5IGEgZGlzY3Vzc2lvbiBvZiB0aGVzZSBjbHVzdGVyaW5nIG1ldGhvZHMgaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIGNvdXJzZS4KCmBgYHtyfQojIFBlcmZvcm0gY2x1c3RlcmluZwppcmlzX2sgPC0ga21lYW5zKGlyaXNbLTVdLCAzKQoKIyBBdXRvcGxvdDogY29sb3IgYWNjb3JkaW5nIHRvIGNsdXN0ZXIKYXV0b3Bsb3QoaXJpc19rLCBkYXRhID0gaXJpcywgZnJhbWUgPSBUUlVFKQoKIyBBdXRvcGxvdDogYWJvdmUsIHBsdXMgc2hhcGUgYWNjb3JkaW5nIHRvIHNwZWNpZXMKYXV0b3Bsb3QoaXJpc19rLCBkYXRhID0gaXJpcywgZnJhbWUgPSBUUlVFLCBzaGFwZT0nU3BlY2llcycpCmBgYAoKV29ya2luZyB3aXRoIG1hcHMgZnJvbSB0aGUgbWFwcyBwYWNrYWdlOiBVU0EKVGhlIGVhc2llc3Qgd2F5IHRvIG9idGFpbiBtYXAgcG9seWdvbnMgaXMgdGhyb3VnaCB0aGUgbWFwcyBwYWNrYWdlLiBVbmZvcnR1bmF0ZWx5IHRoZXJlIGFyZSBvbmx5IGEgZmV3IGxvY2F0aW9ucyBhdmFpbGFibGUsIGJ1dCBpZiB5b3VyIHJlZ2lvbiBvZiBpbnRlcmVzdCBpcyBpbmNsdWRlZCB0aGV5IGFyZSBleHRyZW1lbHkgY29udmVuaWVudC4KClRoZSBhdmFpbGFibGUgbWFwcyBvZiBwb2xpdGljYWwgYm91bmRhcmllcyBhcmU6CgpHbG9iYWw6IHdvcmxkLCB3b3JsZDIKQ291bnRyeTogZnJhbmNlLCBpdGFseSwgbnosIHVzYQpVU0E6IGNvdW50eSwgc3RhdGUKVGhlIG1hcHMgY2FuIGJlIGFjY2Vzc2VkIHZpYSBnZ3Bsb3QyOjptYXBfZGF0YSgpLCB3aGljaCBjb252ZXJ0cyB0aGUgbWFwIGludG8gYSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgdGhlIHZhcmlhYmxlcyBsb25nIGFuZCBsYXQuIFRvIGRyYXcgdGhlIG1hcCwgeW91IG5lZWQgdG8gdXNlIGdlb21fcG9seWdvbigpIHdoaWNoIHdpbGwgY29ubmVjdCB0aGUgcG9pbnRzIG9mIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgZm9yIHlvdS4KCmBgYHtyfQpsaWJyYXJ5KG1hcHMpCiMgbWFwcywgZ2dwbG90MiwgYW5kIGdnbWFwIGFyZSBwcmUtbG9hZGVkCiMgVXNlIG1hcF9kYXRhKCkgdG8gY3JlYXRlIHVzYSBhbmQgaW5zcGVjdAp1c2EgPC0gbWFwX2RhdGEoInVzYSIpCnN0cih1c2EpCgojIEJ1aWxkIHRoZSBtYXAKZ2dwbG90KHVzYSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKwogIGdlb21fcG9seWdvbigpICsKICBjb29yZF9tYXAoKSArCiAgdGhlbWVfbm90aGluZygpCmBgYAoKVGhlIHBvcHVsYXRpb24gcHlyYW1pZApBbmltYXRpb25zIGFyZSBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciB0ZW1wb3JhbCBvciBnZW9zcGF0aWFsIGRhdGEsIGFuZCB0aGV5IGFyZSBzdXJwcmlzaW5nbHkgZWFzeSB0byBtYWtlISBIZXJlLCB5b3Ugc2ltcGx5IGxvb3Agb3ZlciB0aGUgdGltZSB2YXJpYWJsZSBpbiB5b3VyIGRhdGFzZXQsIGNvbXBvc2luZyBhIG5ldyBwbG90IGZvciBlYWNoIHN1YnNldCBpbiB0aGUgZGF0YS4gVGhlc2UgaW5kaXZpZHVhbCBpbWFnZXMgYXJlIHRoZW4gY2F0YWxvZ2VkIGluIGFuIGFuaW1hdGVkIEdJRiBmaWxlLgoKVG8gc2hvdyB0aGlzIHlvdSdsbCB1c2UgYSBncmVhdCBhbmltYXRlZCBwb3B1bGF0aW9uIHB5cmFtaWQgdGhhdCB3YXMgcHJlc2VudGVkIG9uIHRoZSBSZXZvbHV0aW9ucyBibG9nLiBUaGVyZSBhcmUgbWFueSBtb3JlIGFkanVzdG1lbnRzIHlvdSBjb3VsZCBoYXZlIG1hZGUgdG8gdGhlIHBsb3QsIGJ1dCB3ZSdsbCBqdXN0IG1ha2UgYSBiYXJlYm9uZXMgdmVyc2lvbiBoZXJlLgoKYGBge3J9CmphcGFuIDwtIHJlYWQudGFibGUoImphcGFuUE9QLnR4dCIsIGhlYWRlcj1UUlVFKQpoZWFkKGphcGFuKQpgYGAKCmBgYHtyfQojIEluc3BlY3Qgc3RydWN0dXJlIG9mIGphcGFuCnN0cihqYXBhbikKbGlicmFyeShhbmltYXRpb24pCiMgRmluaXNoIHRoZSBjb2RlIGluc2lkZSBzYXZlR0lGCnNhdmVHSUYoewoKICAjIExvb3AgdGhyb3VnaCBhbGwgdGltZSBwb2ludHMKICBmb3IgKGkgaW4gdW5pcXVlKGphcGFuJHRpbWUpKSB7CgogICAgIyBTdWJzZXQgamFwYW46IGRhdGEKICAgIGRhdGEgPC0gc3Vic2V0KGphcGFuLCB0aW1lID09IGkpCgogICAgIyBGaW5pc2ggdGhlIGdncGxvdCBjb21tYW5kCiAgICBwIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IEFHRSwgeSA9IFBPUCwgZmlsbCA9IFNFWCwgd2lkdGggPSAxKSkgKwogICAgICBjb29yZF9mbGlwKCkgKwogICAgICBnZW9tX2JhcihkYXRhID0gZGF0YVtkYXRhJFNFWCA9PSAiRmVtYWxlIixdLCBzdGF0ID0gImlkZW50aXR5IikgKwogICAgICBnZW9tX2JhcihkYXRhID0gZGF0YVtkYXRhJFNFWCA9PSAiTWFsZSIsXSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2d0aXRsZShpKQoKICAgIHByaW50KHApCgogIH0KCn0sIG1vdmllLm5hbWUgPSAicHlyYW1pZC5naWYiLCBpbnRlcnZhbCA9IDAuMSkKYGBgCgpBcnJhbmdpbmcgcGxvdHMgKDEpClRoZSBmdW5jdGlvbnMgaW4gZ3JpZEV4dHJhIGFsbG93IHlvdSB0byBhcnJhbmdlIGFueSBudW1iZXIgb2YgcGxvdHMgaW4gYSB2YXJpZXR5IG9mIHdheXMuIFNpbmNlIHlvdSBjYW4gYWNjZXNzIHRoZSBsZWdlbmQgYXMgYSBzZXBhcmF0ZSBvYmplY3QsIHRoYXQgbWVhbnMgeW91IGNhbiBhbHNvIGFycmFuZ2UgbXVsdGlwbGUgcGxvdHMgd2l0aCBhIHNpbmdsZSBsZWdlbmQsIGFzIHNob3duIGluIHRoZSB2aWV3ZXIuIFRoaXMgaXMgYSBnb29kIGFsdGVybmF0aXZlIHRvIGZhY2V0aW5nLCBzaW5jZSB3aXRoIGZhY2V0cyBpdCdzIG5vdCBwb3NzaWJsZSB0byBzZXQgYSBkaWZmZXJlbnQgZ2VvbSBmb3IgZWFjaCBzdWItcGxvdC4gSGVyZSwgeW91IGNhbiBjb21iaW5lIGFueSB2YXJpZXR5IG9mIHBsb3RzIGFuZCB1c2UgYSBjb25zaXN0ZW50IGNvbG9yIHNjYWxlIHdpdGggb25seSBvbmUgbGVnZW5kIHRvIHVuaWZ5IHRoZSB3aG9sZSBpbWFnZS4KClRvIGRvIHRoaXMgeW91J2xsIGNyZWF0ZSBhIG5ldyBhcnJhbmdlIGdyYXBoaWNhbCBvYmplY3QsIHVzaW5nIGdyaWQuYXJyYW5nZSgpLCB3aGljaCB3aWxsIGNvbWJpbmUgc2V2ZXJhbCBwcmUtZXhpc3RpbmcgZ3JvYnMuIEp1c3QgbGlrZSB3aXRoIGdyaWQucmVjdCgpIGFuZCByZWN0R3JvYigpIHRoZXJlIGFyZSB0d28gdmVyc2lvbnMgb2YgdGhlIGFycmFuZ2UgZ3JvYiwgb25lIGdyaWQuYXJyYW5nZSgpIHByb2R1Y2VzIGEgZ3JhcGhpY3Mgb3V0cHV0LCB3aGljaCBtZWFucyB5b3UganVzdCBkcmF3IHRoZSBpdGVtIHRvIHRoZSB2aWV3ZXIsIGFuZCBhcnJhbmdlR3JvYigpIHdoaWNoIHJldHVybnMgYSBncmFwaGljYWwgb2JqZWN0LCBha2EgZ3JvYiB3aGljaCBjYW4gYmUgZnVydGhlciBtYW5pcHVsYXRlZC4KCkluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCBqdXN0IGNyZWF0ZSB5b3VyIG9iamVjdHMgYW5kIGFycmFuZ2UgdGhlbSB1c2luZyBncmlkLmFycmFuZ2UoKS4gSW4gdGhlIGZpcnN0IHN0ZXBzIHlvdSBjcmVhdGVkIHR3byBiYXNpYyBwbG90cywgZzEgYW5kIGcyLiBJbiB0aGUgbmV4dCBleGVyY2lzZSB5b3UnbGwgc2VlIHdoYXQgdG8gZG8gYWJvdXQgdGhlIGxlZ2VuZC4KCmBgYHtyfQojIEFkZCBhIHRoZW1lIChsZWdlbmQgYXQgdGhlIGJvdHRvbSkKZzEgPC0gZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcsIGNvbCA9IGN5bCkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgojIEFkZCBhIHRoZW1lIChubyBsZWdlbmQpCmcyIDwtIGdncGxvdChtdGNhcnMsIGFlcyhkaXNwLCBmaWxsID0gY3lsKSkgKwogIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjUsIGJpbndpZHRoID0gMjApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIExvYWQgZ3JpZEV4dHJhCmxpYnJhcnkoZ3JpZEV4dHJhKQoKIyBDYWxsIGdyaWQuYXJyYW5nZSgpCmdyaWQuYXJyYW5nZShnMSwgZzIsIG5jb2wgPSAyKQpgYGAKCkFycmFuZ2luZyBwbG90cyAoMikKSW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIHlvdSBkaWQgYSBiYXJlLWJvbmVzIGFycmFuZ2VtZW50IG9mIHBsb3RzLCBidXQgaXQgd291bGQgYmUgbmljZXIgaWYgdGhlIHBsb3QgbG9va3MgbGlrZSB0aGUgb25lIHRoYXQncyBzaG93biBpbiB0aGUgdmlld2VyLiBZb3UgY2FuIGltYWdpbmUgdGhhdCB5b3UgaGF2ZSB0aHJlZSBwYW5lbHMsIG5vdCB0d28uIFRoZXJlIGFyZSB0d28gYXN5bW1ldHJpY2FsIHJvd3MsIHRoZSBzbWFsbCBzZWNvbmQgcm93IGlzIHdoZXJlIHRoZSBsZWdlbmQgaXMsIGFuZCB0d28gc3ltbWV0cmljYWwgY29sdW1ucywgd2hlcmUgdGhlIHBsb3RzIGFyZS4KClRvIG9idGFpbiB0aGlzIHBsb3QgeW91IG5lZWQgdG8gZXh0cmFjdCB0aGUgbGVnZW5kLiBZb3UgYWxyZWFkeSBzYXcgdGhpcyBpbiBwcmV2aW91cyBleGVyY2lzZXMgYW5kIGl0IGhhcyBhbHJlYWR5IGJlZW4gZG9uZSBmb3IgeW91OyB0aGUgbGVnZW5kIGlzIGF2YWlsYWJsZSBhcyBteV9sZWdlbmQuIE5leHQgeW91IG5lZWQgdG8gYXJyYW5nZSBhbGwgdGhlIGl0ZW1zIGFwcHJvcHJpYXRlbHkuCgpgYGB7cn0KIyBnZ3Bsb3QyLCBncmlkIGFuZCBncmlkRXh0cmEgaGF2ZSBiZWVuIGxvYWRlZCBmb3IgeW91CiMgRGVmaW5pdGlvbnMgb2YgZzEgYW5kIGcyCmxpYnJhcnkoImdyaWQiKQpnMSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZywgY29sID0gY3lsKSkgKwogIGdlb21fcG9pbnQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpnMiA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZGlzcCwgZmlsbCA9IGN5bCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKbGVnZW5kX2luZGV4IDwtIDE1CiMgRXh0cmFjdCB0aGUgbGVnZW5kIGZyb20gZzEKbXlfbGVnZW5kIDwtIGdncGxvdEdyb2IoZzEpJGdyb2JzW1tsZWdlbmRfaW5kZXhdXSAgCgojIENyZWF0ZSBnMV9ub2xlZwpnMV9ub2xlZyA8LSBnMSArIAogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBDYWxjdWxhdGUgdGhlIGhlaWdodDogbGVnZW5kX2hlaWdodApsZWdlbmRfaGVpZ2h0IDwtIHN1bShteV9sZWdlbmQkaGVpZ2h0cykKCiMgQXJyYW5nZSBnMV9ub2xlZywgZzIgYW5kIG15X2xlZ2VuZApncmlkLmFycmFuZ2UoZzFfbm9sZWcsIGcyLCBteV9sZWdlbmQsCiAgICAgICAgICAgICBsYXlvdXRfbWF0cml4ID0gbWF0cml4KGMoMSwgMywgMiwgMyksIG5jb2wgPSAyKSwKICAgICAgICAgICAgIGhlaWdodHMgPSB1bml0LmModW5pdCgxLCAibnBjIikgLSBsZWdlbmRfaGVpZ2h0LCBsZWdlbmRfaGVpZ2h0KSkKCmBgYAoKQmFzZSBwYWNrYWdlIGJhZyBwbG90CkJlZm9yZSB5b3UgY3JlYXRlIHlvdXIgb3duIHN0YXRzIGxheWVyLCB5b3UnbGwgYmVnaW4gYnkgdW5kZXJzdGFuZGluZyB3aGF0IGEgYmFnIHBsb3QgaXMsIGFuZCBob3cgdG8gZ2V0IHRoZSBkYXRhIGZvciB5b3VyIG93biBwbG90cy4KCkZvciB0aGlzIHlvdSdsbCB1c2UgYSBmYWtlIGRhdGFzZXQgY2FsbGVkIHRlc3RfZGF0YSwgd2hpY2ggb25seSBjb250YWlucyB0d28gdmFyaWFibGVzLiBBIHNjYXR0ZXIgcGxvdCBpcyBzaG93biBpbiB0aGUgdmlld2VyLgoKVGhlIGFwbHBhY2sgcGFja2FnZSwgd2hpY2ggY29udGFpbnMgdGhlIGJhZ3Bsb3QoKSBhbmQgY29tcHV0ZS5iYWdwbG90KCkgZnVuY3Rpb25zLCBoYXMgYmVlbiBsb2FkZWQgZm9yIHlvdS4KCmBgYHtyfQpsaWJyYXJ5KGFwbHBhY2spCnggPSBmbG9vcihydW5pZig2MCwgMTcwMCwgMzcwMCApKQp5ID0gZmxvb3IocnVuaWYoNjAsIDUwLCAzMzApKQp0ZXN0X2RhdGEgPC0gZGF0YS5mcmFtZSgieCIgPSB4LCAieSIgPSB5KQp0ZXN0X2RhdGEKYGBgCmBgYHtyfQojIENhbGwgYmFncGxvdCgpIG9uIHRlc3RfZGF0YQpiYWdwbG90KHRlc3RfZGF0YSkKCiMgQ2FsbCBjb21wdXRlLmJhZ3Bsb3Qgb24gdGVzdF9kYXRhLCBhc3NpZ24gdG8gYmFnCmJhZyA8LSBjb21wdXRlLmJhZ3Bsb3QodGVzdF9kYXRhKQoKIyBEaXNwbGF5IGluZm9ybWF0aW9uCmJhZyRodWxsLmxvb3AKYmFnJGh1bGwuYmFnCmJhZyRweHkub3V0bGllcgoKIyBIaWdobGlnaHQgY29tcG9uZW50cwpwb2ludHMoYmFnJGh1bGwubG9vcCwgY29sID0gImdyZWVuIiwgcGNoID0gMTYpCnBvaW50cyhiYWckaHVsbC5iYWcsIGNvbCA9ICJvcmFuZ2UiLCBwY2ggPSAxNikKcG9pbnRzKGJhZyRweHkub3V0bGllciwgY29sID0gInB1cnBsZSIsIHBjaCA9IDE2KQpgYGAKCk11bHRpbGF5ZXIgZ2dwbG90MiBiYWcgcGxvdApUaGUgdmlld2VyIHNob3dzIHRoZSBwbG90IHlvdSBjcmVhdGVkIGluIHRoZSBsYXN0IGV4ZXJjaXNlLgoKV2l0aCBvdXIgY3VycmVudCB1bmRlcnN0YW5kaW5nLCBpZiB3ZSB3YW50ZWQgdG8gbWFrZSBhIGJhZyBwbG90IGluIGdncGxvdDIsIHdlJ2QgdGFrZSB0aGUgdGhyZWUgZGF0YSBmcmFtZXMgKGZvciB0aGUgbG9vcCwgYmFnIGFuZCBvdXRsaWVycykgYW5kIGFkZCB0aGVtIHVzaW5nIHRocmVlIHNlcGFyYXRlIGdlb20gbGF5ZXJzLgoKTGV0J3Mgc2VlIGhvdyB0aGlzIHNpbXBsZSBzb2x1dGlvbiB3b3JrcyBhbmQgaW4gdGhlIG5leHQgZXhlcmNpc2VzIHlvdSdsbCBleHBhbmQgb24gdGhpcyB0b3BpYyB0byBtYWtlIGEgcmVhbCBzdGF0cyBsYXllci4KClRoZSBiYWcgYW5kIHRlc3RfZGF0YSBvYmplY3RzIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGFyZSBwcm92aWRlZC4gdGVzdF9kYXRhIGNvbnRhaW5zIHR3byB2YXJpYWJsZXM6IHggYW5kIHkuCgpgYGB7cn0KIyBiYWcgYW5kIHRlc3RfZGF0YSBhcmUgYXZhaWxhYmxlCgojIENyZWF0ZSBkYXRhIGZyYW1lcyBmcm9tIG1hdHJpY2VzCmh1bGwubG9vcCA8LSBkYXRhLmZyYW1lKHggPSBiYWckaHVsbC5sb29wWywxXSwgeSA9IGJhZyRodWxsLmxvb3BbLDJdKQpodWxsLmJhZyA8LSBkYXRhLmZyYW1lKHggPSBiYWckaHVsbC5iYWdbLDFdLCB5ID0gYmFnJGh1bGwuYmFnWywyXSkKcHh5Lm91dGxpZXIgPC0gZGF0YS5mcmFtZSh4ID0gYmFnJHB4eS5vdXRsaWVyWywxXSwgeSA9IGJhZyRweHkub3V0bGllclssMl0pCgojIEZpbmlzaCB0aGUgZ2dwbG90IGNvbW1hbmQKZ2dwbG90KHRlc3RfZGF0YSwgYWVzKHggPSB4LCAgeSA9IHkpKSArCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSBodWxsLmxvb3AsIGZpbGwgPSAiZ3JlZW4iKSArCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSBodWxsLmJhZywgZmlsbCA9ICJvcmFuZ2UiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcHh5Lm91dGxpZXIsIGNvbCA9ICJwdXJwbGUiLCBwY2ggPSAxNiwgY2V4ID0gMS41KQpgYGAKCkNyZWF0aW5nIGdncHJvdG8gZnVuY3Rpb25zCk5vdyB0aGF0IHlvdSBrbm93IHdoZXJlIHRvIGZpbmQgdGhlIHN0YXRpc3RpY3MgYW5kIGhvdyB0byB1c2UgdGhlbSBpbiBnZ3Bsb3QyLCBsZXQncyBwdXQgdGhlbSBpbnRvIHRoZSBmdW5jdGlvbnMgdGhhdCB3aWxsIG1ha2UgdGhlbSBlYXNpZXIgdG8gdXNlLgoKRm9yIHRoaXMgeW91J2xsIHVzZSB0aGUgZ2dwcm90byBvYmplY3Qtb3JpZW50ZWQgcHJvZ3JhbW1pbmcgc3lzdGVtIC0gdGhlIGJhc2lzIG9mIGNyZWF0aW5nIGEgbmV3IGxheWVyIGluIGdncGxvdDIuIFRoZXJlIGFyZSBmb3VyIGFyZ3VtZW50cyBmb3IgYSBnZ3Byb3RvIG9iamVjdC4gVGhlIGZpcnN0IHR3byBhcmd1bWVudHMgYXJlIGl0cyBuYW1lIGFuZCB3aGF0IGl0IGluaGVyaXRzIGZyb20gKGluIHRoaXMgY2FzZSBTdGF0KS4gTmV4dCBjb21lIHRoZSByZXF1aXJlZCBhZXN0aGV0aWNzLCBhbmQgdGhlbiwgbW9zdCBpbXBvcnRhbnRseSwgd2hhdCB0aGUgc3RhdCBzaG91bGQgZG8uIEZvciBlYWNoIGdyb3VwIG9mIGRhdGEgaXQgcmVjZWl2ZXMgZnJvbSB0aGUgZGF0YSBsYXllciwgd2hhdCBzaG91bGQgYmUgY29tcHV0ZWQ/IFRoaXMgd2lsbCBzaW1wbHkgYmUgdGhlIGNhbGN1bGF0aW9ucyB5b3UgcGVyZm9ybWVkIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZS4KClRoZSBnZ3Byb3RvIG9iamVjdCBkZWZpbml0aW9uIG9mIFN0YXRMb29wIGlzIGFscmVhZHkgcHJvdmlkZWQuIENhbiB5b3UgZmluaXNoIHRoZSBpbXBsZW1lbnRhdGlvbnMgZm9yIHRoZSBvdGhlciBvbmVzPwoKYGBge3J9CiMgZ2dwcm90byBmb3IgU3RhdExvb3AgKGh1bGwubG9vcCkKU3RhdExvb3AgPC0gZ2dwcm90bygiU3RhdExvb3AiLCBTdGF0LAogICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2FlcyA9IGMoIngiLCAieSIpLAogICAgICAgICAgICAgICAgICAgIGNvbXB1dGVfZ3JvdXAgPSBmdW5jdGlvbihkYXRhLCBzY2FsZXMpIHsKICAgICAgICAgICAgICAgICAgICAgIGJhZyA8LSBjb21wdXRlLmJhZ3Bsb3QoeCA9IGRhdGEkeCwgeSA9IGRhdGEkeSkKICAgICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoeCA9IGJhZyRodWxsLmxvb3BbLDFdLCB5ID0gYmFnJGh1bGwubG9vcFssMl0pCiAgICAgICAgICAgICAgICAgICAgfSkKCiMgZ2dwcm90byBmb3IgU3RhdEJhZyAoaHVsbC5iYWcpClN0YXRCYWcgPC0gZ2dwcm90bygiU3RhdEJhZyIsIFN0YXQsCiAgICAgICAgICAgICAgICAgICByZXF1aXJlZF9hZXMgPSBjKCJ4IiwgInkiKSwKICAgICAgICAgICAgICAgICAgIGNvbXB1dGVfZ3JvdXAgPSBmdW5jdGlvbihkYXRhLCBzY2FsZXMpIHsKICAgICAgICAgICAgICAgICAgICAgYmFnIDwtIGNvbXB1dGUuYmFncGxvdCh4ID0gZGF0YSR4LCB5ID0gZGF0YSR5KQogICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKHggPSBiYWckaHVsbC5iYWdbLDFdLCB5ID0gYmFnJGh1bGwuYmFnWywyXSkKICAgICAgICAgICAgICAgICAgIH0pCgojIGdncHJvdG8gZm9yIFN0YXRPdXQgKHB4eS5vdXRsaWVyKQpTdGF0T3V0IDwtIGdncHJvdG8oIlN0YXRPdXQiLCBTdGF0LAogICAgICAgICAgICAgICAgICAgcmVxdWlyZWRfYWVzID0gYygieCIsICJ5IiksCiAgICAgICAgICAgICAgICAgICBjb21wdXRlX2dyb3VwID0gZnVuY3Rpb24oZGF0YSwgc2NhbGVzKSB7CiAgICAgICAgICAgICAgICAgICAgIGJhZyA8LSBjb21wdXRlLmJhZ3Bsb3QoeCA9IGRhdGEkeCwgeSA9IGRhdGEkeSkKICAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZSh4ID0gYmFnJHB4eS5vdXRsaWVyWywxXSwgeSA9IGJhZyRweHkub3V0bGllclssMl0pCiAgICAgICAgICAgICAgICAgICB9KQpgYGAKCkNyZWF0aW5nIHN0YXRfYmFnKCkKSW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIHlvdSBlc3RhYmxpc2hlZCB0aHJlZSBnZ3Byb3RvIG9iamVjdHMsIG5vdyB5b3UgbmVlZCB0byBjb21iaW5lIHRoZW0gdW5kZXIgYSBuZXcgZ2dwbG90MiBmdW5jdGlvbiB0aGF0IHlvdSdsbCBjYWxsIHN0YXRfYmFnKCkuCgpBZGRpbmcgYSBzdGF0X2JhZygpIGxheWVyIHdpbGwgZXhlY3V0ZSBlYWNoIG9mIHRoZSB0aHJlZSBnZ3Byb3RvIG9iamVjdHMgdGhhdCB5b3UganVzdCBjcmVhdGVkLgoKWW91ciB0aHJlZSBvYmplY3RzIGFyZSBjYWxsZWQgU3RhdExvb3AsIFN0YXRCYWcsIFN0YXRPdXQsIHNvIHlvdSdsbCBuZWVkIHRocmVlIGxheWVycyBpbiB5b3VyIHN0YXRfYmFnKCkgZnVuY3Rpb24sIHdoaWNoIHlvdSdsbCBtYWtlIHdpdGggdGhlIGxheWVyKCkgZnVuY3Rpb24uIFdoZW4geW91IGhhdmUgbXVsdGlwbGUgbGF5ZXJzLCB5b3UgY2FuIGNvbWJpbmUgdGhlbSBpbiBhIGxpc3QgYnkgc2ltcGx5IGNhbGxpbmcgbGlzdCgpLgoKRm9yIGVhY2ggbGF5ZXIsIHlvdSdsbCBhbHNvIG5lZWQgdG8gc3BlY2lmeSB0aGUgYXBwcm9ycGlhdGUgZ2VvbTogInBvbHlnb24iIG9yICJwb2ludCIuCgpUaGUgZnJhbWV3b3JrIGZvciB0aGUgc3RhdF9iYWcoKSBsYXllciBmdW5jdGlvbiBoYXMgYmVlbiBwcm92aWRlZCBmb3IgeW91LgoKYGBge3J9CiMgU3RhdExvb3AsIFN0YXRCYWcgYW5kIFN0YXRPdXQgYXJlIGF2YWlsYWJsZQoKIyBDb21iaW5lIGdncHJvdG8gb2JqZWN0cyBpbiBsYXllcnMgdG8gYnVpbGQgc3RhdF9iYWcoKQpzdGF0X2JhZyA8LSBmdW5jdGlvbihtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsIGdlb20gPSAicG9seWdvbiIsCiAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gImlkZW50aXR5IiwgbmEucm0gPSBGQUxTRSwgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBUUlVFLCBsb29wID0gRkFMU0UsIC4uLikgewogIGxpc3QoCiAgICAjIFN0YXRMb29wIGxheWVyCiAgICBsYXllcigKICAgICAgc3RhdCA9IFN0YXRMb29wLCBkYXRhID0gZGF0YSwgbWFwcGluZyA9IG1hcHBpbmcsIGdlb20gPSBnZW9tLCAKICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbiwgc2hvdy5sZWdlbmQgPSBzaG93LmxlZ2VuZCwgaW5oZXJpdC5hZXMgPSBpbmhlcml0LmFlcywKICAgICAgcGFyYW1zID0gbGlzdChuYS5ybSA9IG5hLnJtLCBhbHBoYSA9IDAuMzUsIGNvbCA9IE5BLCAuLi4pCiAgICApLAogICAgIyBTdGF0QmFnIGxheWVyCiAgICBsYXllcigKICAgICAgc3RhdCA9IFN0YXRCYWcsIGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gbWFwcGluZywgZ2VvbSA9IGdlb20sIAogICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uLCBzaG93LmxlZ2VuZCA9IHNob3cubGVnZW5kLCBpbmhlcml0LmFlcyA9IGluaGVyaXQuYWVzLAogICAgICBwYXJhbXMgPSBsaXN0KG5hLnJtID0gbmEucm0sIGFscGhhID0gMC4zNSwgY29sID0gTkEsIC4uLikKICAgICksCiAgICAjIFN0YXRPdXQgbGF5ZXIKICAgIGxheWVyKAogICAgICBzdGF0ID0gU3RhdE91dCwgZGF0YSA9IGRhdGEsIG1hcHBpbmcgPSBtYXBwaW5nLCBnZW9tID0gInBvaW50IiwgCiAgICAgIHBvc2l0aW9uID0gcG9zaXRpb24sIHNob3cubGVnZW5kID0gc2hvdy5sZWdlbmQsIGluaGVyaXQuYWVzID0gaW5oZXJpdC5hZXMsCiAgICAgIHBhcmFtcyA9IGxpc3QobmEucm0gPSBuYS5ybSwgYWxwaGEgPSAwLjcsIGNvbCA9IE5BLCBzaGFwZSA9IDIxLCAuLi4pCiAgICApCiAgKQp9CmBgYAoKVXNlIHN0YXRfYmFnKCkKU28gZmFyIHlvdSd2ZSBzZWVuIHRoZSBiYXNpY3MgZm9yIGNyZWF0aW5nIGEgbmV3IGdncGxvdCBsYXllci4gSXQncyBiYXJlLWJvbmVzLCBidXQgZnVuY3Rpb25hbC4gWW91IG5vdyBoYXZlIGEgd29ya2luZyBzb2x1dGlvbiB0byB0aGUgYmFnIHBsb3QgcXVlc3Rpb24uCgpUaGUgZ2dwbG90MiBjb21tYW5kIHRoYXQgeW91J3ZlIGNvZGVkIGJlZm9yZSBpcyBhdmFpbGFibGUuIG5vdywgbGV0J3MgdXNlIHN0YXRfYmFnKCkgdG8gbWFrZSBvdXIgcGxvdCEKCmBgYHtyfQojIGh1bGwubG9vcCwgaHVsbC5iYWcgYW5kIHB4eS5vdXRsaWVyIGFyZSBhdmFpbGFibGUKIyBzdGF0X2JhZywgdGVzdF9kYXRhIGFuZCB0ZXN0X2RhdGEyIGFyZSBhdmFpbGFibGUKCiMgUHJldmlvdXMgbWV0aG9kCmdncGxvdCh0ZXN0X2RhdGEsIGFlcyh4ID0geCwgIHkgPSB5KSkgKwogIGdlb21fcG9seWdvbihkYXRhID0gaHVsbC5sb29wLCBmaWxsID0gImdyZWVuIikgKwogIGdlb21fcG9seWdvbihkYXRhID0gaHVsbC5iYWcsIGZpbGwgPSAib3JhbmdlIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IHB4eS5vdXRsaWVyLCBjb2wgPSAicHVycGxlIiwgcGNoID0gMTYsIGNleCA9IDEuNSkKCiMgc3RhdF9iYWcKZ2dwbG90KHRlc3RfZGF0YSwgYWVzKHggPSB4LCB5ID0geSkpICsKICBzdGF0X2JhZyhmaWxsID0gJ2JsYWNrJykKYGBgCgpWaWV3cG9ydCBiYXNpY3MgKDEpClRvIGdldCBmYW1pbGlhciB3aXRoIGdyaWQgZ3JhcGhpY3MsIHlvdSdsbCBiZWdpbiB3aXRoIHVzaW5nIHNvbWUgZ3JpZC4gZnVuY3Rpb25zLiBUaGUgZ3JpZCBwYWNrYWdlIGlzIGFscmVhZHkgbG9hZGVkIGludG8geW91ciBSIHNlc3Npb24sIHNvIHlvdSBjYW4gZ2V0IHN0YXJ0ZWQgc3RyYWlnaHQgYXdheSEKCk5vdGU6IEluIERhdGFDYW1wJ3MgbGVhcm5pbmcgaW50ZXJmYWNlLCBlYWNoIGNoYW5nZSB5b3UgbWFrZSB0byB0aGUgcGxvdCB3aWxsIGFwcGVhciBhcyBhIG5ldyBwbG90LCBzbyB5b3UgY2FuIHNlZSB0aGUgZWZmZWN0IG9mIGVhY2ggY29tbWFuZC4KCmBgYHtyfQojIERyYXcgcmVjdGFuZ2xlIGluIG51bGwgdmlld3BvcnQKZ3JpZC5yZWN0KGdwID0gZ3BhcihmaWxsID0gImdyZXk5MCIpKQoKIyBXcml0ZSB0ZXh0IGluIG51bGwgdmlld3BvcnQKZ3JpZC50ZXh0KCJudWxsIHZpZXdwb3J0IikKCiMgRHJhdyBhIGxpbmUKZ3JpZC5saW5lcyh4ID0gYygwLCAwLjc1KSwgeSA9IGMoMC4yNSwgMSksCiAgICAgICAgICBncCA9IGdwYXIobHR5ID0gMiwgY29sID0gInJlZCIpKQoKYGBgCgpWaWV3cG9ydCBiYXNpY3MgKDIpClRoZSBjb2RlIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIHRoYXQgcG9wdWxhdGVzIHRoZSBudWxsIHZpZXdwb3J0IHdpdGggc29tZSBiYXNpYyBzaGFwZXMgaXMgYWxyZWFkeSBhdmFpbGFibGUuIExldCdzIHRha2UgdGhlIG5leHQgc3RlcCBhbmQgc3RhcnQgbWFuaXB1bGF0aW5nIHRoZSBzdGFjayBvZiB2aWV3cG9ydHMuCgpgYGB7cn0KIyBQb3B1bGF0ZSBudWxsIHZpZXdwb3J0CmdyaWQucmVjdChncCA9IGdwYXIoZmlsbCA9ICJncmV5OTAiKSkKZ3JpZC50ZXh0KCJudWxsIHZpZXdwb3J0IikKZ3JpZC5saW5lcyh4ID0gYygwLDAuNzUpLCB5ID0gYygwLjI1LCAxKSwKICAgICAgICAgICBncCA9IGdwYXIobHR5ID0gMiwgY29sID0gInJlZCIpKQoKIyBDcmVhdGUgbmV3IHZpZXdwb3J0OiB2cAp2cCA8LSB2aWV3cG9ydCh4ID0gMC41LCB5ID0gMC41LCB3aWR0aCA9IDAuNSwgaGVpZ2h0ID0gMC41LCBqdXN0ID0gImNlbnRlciIpCgojIFB1c2ggdnAKcHVzaFZpZXdwb3J0KHZwKQoKIyBQb3B1bGF0ZSBuZXcgdmlld3BvcnQgd2l0aCByZWN0YW5nbGUKZ3JpZC5yZWN0KGdwID0gZ3BhcihmaWxsID0gImJsdWUiKSkKYGBgCgpCdWlsZCBhIHBsb3QgZnJvbSBzY3JhdGNoICgxKQpVc2luZyB0aGUgdmlld3BvcnRzLCB5b3UgY2FuIGNyZWF0ZSBwbG90cywgbWFuaXB1bGF0aW5nIHRoZSBzcGFjZSBhcyBuZWVkZWQuCgpJbiB0aGlzIGV4ZXJjaXNlIHlvdSdsbCBlc3RhYmxpc2ggeW91ciBncmlkIHZpZXdwb3J0IGFuZCBpbiB0aGUgZm9sbG93aW5nIGV4ZXJjaXNlIHlvdSdsbCBwb3B1bGF0ZSBpdCB3aXRoIHZhbHVlcy4KCmBgYHtyfQojIDEgLSBDcmVhdGUgcGxvdCB2aWV3cG9ydDogcHZwCm1hciA8LSBjKDUsIDQsIDIsIDIpCnB2cCA8LSBwbG90Vmlld3BvcnQobWFyKQoKIyAyIC0gUHVzaCBwdnAKcHVzaFZpZXdwb3J0KHB2cCkKCiMgMyAtIEFkZCByZWN0YW5nbGUKZ3JpZC5yZWN0KGdwID0gZ3BhcihmaWxsID0gImdyZXk4MCIpKQoKIyBDcmVhdGUgZGF0YSB2aWV3cG9ydDogZHZwCmR2cCA8LSBkYXRhVmlld3BvcnQoeERhdGEgPSBtdGNhcnMkd3QsIHlEYXRhID0gbXRjYXJzJG1wZykKCiMgNCAtIFB1c2ggZHZwCnB1c2hWaWV3cG9ydChkdnApCgojIEFkZCB0d28gYXhlcwpncmlkLnhheGlzKCkKZ3JpZC55YXhpcygpCmBgYAoKCkJ1aWxkIGEgcGxvdCBmcm9tIHNjcmF0Y2ggKDIpClRoZSB3b3JrIHlvdSBkaWQgYmVmb3JlIHRvIGJ1aWxkIGEgcGxvdCBmcm9tIHNjcmF0Y2ggaXMgYWxyZWFkeSBpbmNsdWRlZC4gTm93IHlvdSdyZSByZWFkeSB0byBhZGQgdGhlIHBvaW50cyBhbmQgdGhlIGFwcHJvcHJpYXRlIGxhYmVscy4KCmBgYHtyfQojIFdvcmsgZnJvbSBiZWZvcmUKcHVzaFZpZXdwb3J0KHBsb3RWaWV3cG9ydChjKDUsIDQsIDIsIDIpKSkKZ3JpZC5yZWN0KGdwID0gZ3BhcigpKQpwdXNoVmlld3BvcnQoZGF0YVZpZXdwb3J0KHhEYXRhID0gbXRjYXJzJHd0LCB5RGF0YSA9IG10Y2FycyRtcGcpKQpncmlkLnhheGlzKCkKZ3JpZC55YXhpcygpCgojIDEgLSBBZGQgdGV4dCB0byB4IGF4aXMKZ3JpZC50ZXh0KCJXZWlnaHQiLCB5ID0gdW5pdCgtMywgImxpbmVzIikpCgojIDIgLSBBZGQgdGV4dCB0byB5IGF4aXMKZ3JpZC50ZXh0KCJNUEciLCB4ID0gdW5pdCgtMywgImxpbmVzIiksIHJvdCA9IDkwKQoKIyAzIC0gQWRkIHBvaW50cwpncmlkLnBvaW50cyh4ID0gbXRjYXJzJHd0LCB5ID0gbXRjYXJzJG1wZywgcGNoID0gMTYpCmBgYAoKTW9kaWZ5aW5nIGEgcGxvdCB3aXRoIGdyaWQuZWRpdApUaGUgY29tbWFuZHMgeW91J3ZlIGNvZGVkIHVwIHRvIG5vdyB0byBjcmVhdGUgdGhlIHBsb3QgYXJlIGF2YWlsYWJsZSBpbiB0aGUgZWRpdG9yLiBUaGUgZ3JlYXQgdGhpbmcgYWJvdXQgZ3JpZCwgaW4gY29tcGFyaXNvbiB0byBiYXNlLCBpcyB0aGF0IHlvdSBjYW4gbmFtZSB0aGUgZGlmZmVyZW50IHBsb3QgZWxlbWVudHMsIHNvIHRoYXQgeW91IGNhbiBhY2Nlc3MgdGhlbSBhbmQgY2hhbmdlIHRoZW0gbGF0ZXIgb24uIFlvdSBjYW4gZG8gdGhpcyB3aXRoIHRoZSBncmlkLmVkaXQoKSBmdW5jdGlvbi4gR2l2ZSBpdCBhIHRyeSEKCmBgYHtyfQojIFdvcmsgZnJvbSBiZWZvcmUKcHVzaFZpZXdwb3J0KHBsb3RWaWV3cG9ydChjKDUsIDQsIDIsIDIpKSkKZ3JpZC5yZWN0KGdwID0gZ3BhcigpKQpwdXNoVmlld3BvcnQoZGF0YVZpZXdwb3J0KHhEYXRhID0gbXRjYXJzJHd0LCB5RGF0YSA9IG10Y2FycyRtcGcpKQpncmlkLnhheGlzKCkKZ3JpZC55YXhpcygpCgojIFdvcmsgZnJvbSBiZWZvcmUgLSBhZGQgbmFtZXMKZ3JpZC50ZXh0KCJXZWlnaHQiLCB5ID0gdW5pdCgtMywgImxpbmVzIiksIG5hbWUgPSAieGF4aXMiKQpncmlkLnRleHQoIk1QRyIsIHggPSB1bml0KC0zLCAibGluZXMiKSwgcm90ID0gOTAsIG5hbWUgPSAieWF4aXMiKQpncmlkLnBvaW50cyh4ID0gbXRjYXJzJHd0LCB5ID0gbXRjYXJzJG1wZywgcGNoID0gMTYsIG5hbWUgPSAiZGF0YXBvaW50cyIpCgojIEVkaXQgInhheGlzIgpncmlkLmVkaXQoInhheGlzIiwgbGFiZWwgPSAiV2VpZ2h0ICgxMDAwIGxicykiKQoKIyBFZGl0ICJ5YXhpcyIKZ3JpZC5lZGl0KCJ5YXhpcyIsIGxhYmVsID0gIk1pbGVzLyhVUykgZ2FsbG9uIikKCiMgRWRpdCAiZGF0YXBvaW50cyIKZ3JpZC5lZGl0KCJkYXRhcG9pbnRzIiwgZ3AgPSAoZ3Bhcihjb2wgPSAiI0MzMjEyNzY2IiwgY2V4ID0gMikpKQpgYGAKCkV4cGxvcmluZyB0aGUgZ1RhYmxlCkluIHRoZSBwcmV2aW91cyBjaGFwdGVyIHlvdSBzYXcgZ3JhcGhpY2FsIG91dHB1dHMgdXNpbmcgYSB2YXJpZXR5IG9mIGdyaWQuIGZ1bmN0aW9ucy4gR3JhcGhpY2FsIE9iamVjdHMsIGFrYSBHcm9icywgYXJlIHRoZSBvYmplY3QgZm9ybSBvZiB0aGVzZSBpdGVtcyBhbmQgY2FuIGJlIGZvdW5kIGluIHlvdXIgZ2dwbG90MiBwbG90cy4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgaG93IHRoZXNlIGdyb2JzIGFyZSBzdG9yZWQgaW4gZ2dwbG90IG9iamVjdHMuCgpUbyBzdGFydCwgYSBzaW1wbGUgcGxvdCwgcCwgaGFzIGJlZW4gY29kZWQgZm9yIHlvdS4KCmBgYHtyfQojIEEgc2ltcGxlIHBsb3QgcApwIDwtIGdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbCA9IGZhY3RvcihjeWwpKSkgKyBnZW9tX3BvaW50KCkKCiMgQ3JlYXRlIGd0YWIgd2l0aCBnZ3Bsb3RHcm9iKCkKZ3RhYiA8LSBnZ3Bsb3RHcm9iKHApCgojIFByaW50IG91dCBndGFiCmd0YWIKCiMgRXh0cmFjdCB0aGUgZ3JvYnMgZnJvbSBndGFiOiBndGFiCmcgPC0gZ3RhYiRncm9icwoKIyBEcmF3IG9ubHkgdGhlIGxlZ2VuZApsZWdlbmRfaW5kZXggPC0gd2hpY2godmFwcGx5KGcsIGluaGVyaXRzLCB3aGF0ID0gImd0YWJsZSIsIGxvZ2ljYWwoMSkpKQpncmlkLmRyYXcoZ1tbbGVnZW5kX2luZGV4XV0pCmBgYAoKTW9kaWZ5aW5nIHRoZSBnVGFibGUKWW91IGNhbiB2aXN1YWxpemUgdGhlIGxheW91dCBvZiBhIGdUYWJsZSBvYmplY3Qgd2l0aCBndGFibGVfc2hvd19sYXlvdXQoKS4gSW4gdGhlIGxheW91dCBwbG90LCBlYWNoIHNlZ21lbnQgaXMgbGFiZWxsZWQgd2l0aCBpdHMgcG9zaXRpb24uCgpUaGUgbGVnZW5kLCB0aGF0IHlvdSBjYW4gYWNjZXNzIHdpdGggZ1tbOF1dLCBpcyBhIGdUYWJsZSBpdHNlbGYsIHNvIHlvdSBjYW4gYWxzbyBzaG93IGl0cyBsYXlvdXQuIEl0J3MgcGVyZmVjdGx5IHBvc3NpYmxlIHRvIHVwZGF0ZSB0aGlzIGxheW91dCBieSBhZGRpbmcgbmV3IGdyYXBoaWNhbCBvYmplY3RzLCBzaW1pbGFyIHRvIHdoYXQgeW91IHNhdyBpbiB0aGUgdmlkZW8uCgpgYGB7cn0KbGlicmFyeShndGFibGUpCiMgQ29kZSBmcm9tIGJlZm9yZQpwIDwtIGdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbCA9IGZhY3RvcihjeWwpKSkgKyBnZW9tX3BvaW50KCkKZ3RhYiA8LSBnZ3Bsb3RHcm9iKHApCmcgPC0gZ3RhYiRncm9icwpsZWdlbmRfaW5kZXggPC0gd2hpY2godmFwcGx5KGcsIGluaGVyaXRzLCB3aGF0ID0gImd0YWJsZSIsIGxvZ2ljYWwoMSkpKQpncmlkLmRyYXcoZ1tbbGVnZW5kX2luZGV4XV0pCgojIDEgLSBTaG93IGxheW91dCBvZiBsZWdlbmQgZ3JvYgpndGFibGVfc2hvd19sYXlvdXQoZ1tbbGVnZW5kX2luZGV4XV0pCgojIENyZWF0ZSB0ZXh0IGdyb2IKbXlfdGV4dCA8LSB0ZXh0R3JvYihsYWJlbCA9ICJNb3RvciBUcmVuZCwgMTk3NCIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDcsIGNvbCA9ICJncmF5MjUiKSkKCiMgMiAtIFVzZSBndGFibGVfYWRkX2dyb2IgdG8gbW9kaWZ5IG9yaWdpbmFsIGd0YWIKbmV3X2xlZ2VuZCA8LSBndGFibGVfYWRkX2dyb2IoZ3RhYiRncm9ic1tbbGVnZW5kX2luZGV4XV0sIG15X3RleHQsIDMsIDIpCgojIDMgLSBVcGRhdGUgaW4gZ3RhYgpndGFiJGdyb2JzW1tsZWdlbmRfaW5kZXhdXSA8LSBuZXdfbGVnZW5kCgojIDQgLSBEcmF3IGd0YWIKZ3JpZC5kcmF3KGd0YWIpCmBgYAoKRXhwbG9yaW5nIGdncGxvdCBvYmplY3RzCmdncGxvdCBvYmplY3RzIGFyZSBiYXNpY2FsbHkganVzdCBhIG5hbWVkIGxpc3QgdGhhdCBjb250YWlucyB0aGUgaW5mb3JtYXRpb24gdG8gbWFrZSB0aGUgYWN0dWFsIHBsb3QuIEhlcmUgeW91J2xsIGV4cGxvcmUgdGhlIHN0cnVjdHVyZSBvZiB0aGlzIG9iamVjdC4KCmBgYHtyfQojIFNpbXBsZSBwbG90IHAKcCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnLCBjb2wgPSBmYWN0b3IoY3lsKSkpICsgZ2VvbV9wb2ludCgpCgojIEV4YW1pbmUgY2xhc3MoKSBhbmQgbmFtZXMoKQpjbGFzcyhwKQpuYW1lcyhwKQoKIyBQcmludCB0aGUgc2NhbGVzIHN1Yi1saXN0CnAkc2NhbGVzJHNjYWxlcwoKIyBVcGRhdGUgcApwIDwtIHAgKwogIHNjYWxlX3hfY29udGludW91cygiTGVuZ3RoIiwgbGltaXRzID0gYyg0LCA4KSwgZXhwYW5kID0gYygwLCAwKSkgKwogIHNjYWxlX3lfY29udGludW91cygiV2lkdGgiLCBsaW1pdHMgPSBjKDIsIDQuNSksIGV4cGFuZCA9IGMoMCwgMCkpCgojIFByaW50IHRoZSBzY2FsZXMgc3ViLWxpc3QKcCRzY2FsZXMkc2NhbGVzCmBgYAoKZ2dwbG90X2J1aWxkIGFuZCBnZ3Bsb3RfZ3RhYmxlCkluIHRoZSB2aWV3ZXIgd2UgaGF2ZSBwcm9kdWNlZCBhIGJveCBwbG90IG9mIHRoZSBtdGNhcnMgZGF0YXNldCAoY2FsbGVkIHApIHRoYXQgeW91J2xsIHVzZSB0byBleHBsb3JlIHR3byBrZXkgZ2dwbG90IGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIHRoZSBvYmplY3QgaW50ZXJuYWxzOiBnZ3Bsb3RfYnVpbGQoKSBhbmQgZ2dwbG90X2d0YWJsZSgpLgoKZ2dwbG90X2J1aWxkKCkgaXMgZXhlY3V0ZWQgd2hlbiB5b3Ugd2FudCB0byBkaXNwbGF5IG9yIHNhdmUgYW4gYWN0dWFsIGdncGxvdCBwbG90LiBJdCB0YWtlcyB0aGUgZGF0YSBpbnB1dCBhbmQgcHJvZHVjZXMgdGhlIHZpc3VhbCBvdXRwdXQuCgpgYGB7cn0KIyBCb3ggcGxvdCBvZiBtdGNhcnM6IHAKcCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IGZhY3RvcihjeWwpLCB5ID0gd3QpKSArIGdlb21fYm94cGxvdCgpCgojIENyZWF0ZSBwYnVpbGQKcGJ1aWxkIDwtIGdncGxvdF9idWlsZChwKQoKIyBhIGxpc3Qgb2YgMyBlbGVtZW50cwpuYW1lcyhwYnVpbGQpCgojIFByaW50IG91dCBlYWNoIGVsZW1lbnQgaW4gcGJ1aWxkCnBidWlsZCRkYXRhCnBidWlsZCRwYW5lbApwYnVpbGQkcGxvdAoKIyBDcmVhdGUgZ3RhYiBmcm9tIHBidWlsZApndGFiIDwtIGdncGxvdF9ndGFibGUocGJ1aWxkKQoKIyBEcmF3IGd0YWIKZ3JpZC5kcmF3KGd0YWIpCmBgYAoKRXh0cmFjdGluZyBkZXRhaWxzCkluIHRoZSB2aWRlbyB5b3Ugc2F3IGhvdyB0byBjaGFuZ2UgdGhlIGNsaXBwaW5nIHBhcmFtZXRlcnMgb2YgYSBnVGFibGUgb2JqZWN0LiBIZXJlLCB5b3UnbGwgc2VlIHNvbWV0aGluZyBtb3JlIHByYWN0aWNhbDogaG93IHRvIGV4dHJhY3QgY2FsY3VsYXRlZCB2YWx1ZXMuCgpNYW55IGdlb21zIGFyZSBhc3NvY2lhdGVkIHdpdGggdW5kZXJseWluZyBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIHdoaWNoIGFyZSBjYWxjdWxhdGVkIGFuZCB0aGVuIHBsb3R0ZWQuIEluIHRoZXNlIGNhc2VzIHlvdSBhY3R1YWxseSBkb24ndCBoYXZlIHRoZSBhY3R1YWwgdmFsdWVzIHRoYXQgd2VyZSBwbG90dGVkLiBPZiBjb3Vyc2UsIHRoZXNlIHZhbHVlcyBhcmUgc3RvcmVkIHVuZGVyIHRoZSBob29kIGFuZCB5b3UgY2FuIGFjY2VzcyB0aGVtIGluIHRoZSByZXN1bHRzIGZyb20gZ2dwbG90X2J1aWxkKCkuIFRoaXMgY2FuIGJlIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIGJveCBwbG90cy4gRm9yIGV4YW1wbGUsIHNpbmNlIHRoZXJlIGFyZSBtYW55IG1ldGhvZHMgZm9yIGNhbGN1bGF0aW5nIFExIGFuZCBRMywgaWYgeW91IGNhbGN1bGF0ZSB5b3VyIElRUiBhbmQgb3V0bGllcnMgb3V0c2lkZSBvZiBnZ3Bsb3QyIHlvdSBtYXkgZW5kIHVwIHVzaW5nIGEgZGlmZmVyZW50IG1ldGhvZCBhbmQgZ2V0IGRpZmZlcmVudCByZXN1bHRzLiBTb21ldGltZXMgeW91IHdhbnQgdG8gaGF2ZSBleGFjdGx5IHRoZSB2YWx1ZXMgdGhhdCB3ZXJlIHBsb3R0ZWQuCgpgYGB7cn0KIyBCb3ggcGxvdCBvZiBtdGNhcnM6IHAKcCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IGZhY3RvcihjeWwpLCB5ID0gd3QpKSArIGdlb21fYm94cGxvdCgpCgojIEJ1aWxkIHBkYXRhCnBkYXRhIDwtIGdncGxvdF9idWlsZChwKSRkYXRhCgojIGNvbmZpcm0gdGhhdCB0aGUgZmlyc3QgZWxlbWVudCBvZiB0aGUgbGlzdCBpcyBhIGRhdGEgZnJhbWUKY2xhc3MocGRhdGFbWzFdXSkKCiMgSXNvbGF0ZSB0aGlzIGRhdGEgZnJhbWUKbXlfZGYgPC0gcGRhdGFbWzFdXQoKIyBUaGUgeCBsYWJlbHMKbXlfZGYkZ3JvdXAgPC0gYygiNCIsICI2IiwgIjgiKQoKIyBQcmludCBvdXQgc3BlY2lmaWMgdmFyaWFibGVzCm15X2RmW2MoMTo2LCAxMSldCmBgYAoKCg==